diff -u --recursive --new-file v2.3.6/linux/CREDITS linux/CREDITS
--- v2.3.6/linux/CREDITS	Tue Jun  8 10:27:18 1999
+++ linux/CREDITS	Wed Jun 16 19:26:27 1999
@@ -624,6 +624,13 @@
 S: Oak Park, Illinois 60302
 S: USA
 
+N: Daniel J. Frasnelli
+E: dfrasnel@alphalinux.org
+W: http://www.alphalinux.org/
+P: 1024/3EF87611 B9 F1 44 50 D3 E8 C2 80  DA E5 55 AA 56 7C 42 DA
+D: DEC Alpha hacker 
+D: Miscellaneous bug squisher
+
 N: Jim Freeman
 E: jfree@sovereign.org
 W: http://www.sovereign.org/
diff -u --recursive --new-file v2.3.6/linux/Documentation/ARM-README linux/Documentation/ARM-README
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.6/linux/Documentation/Configure.help	Wed Jun  9 16:59:15 1999
+++ linux/Documentation/Configure.help	Thu Jun 17 01:11:35 1999
@@ -7723,13 +7723,6 @@
   want), say M here and read Documentation/modules.txt. The module
   will be called smbfs.o. Most people say N, however.
 
-SMB Win95 bug work-around
-CONFIG_SMB_WIN95
-  If you want to connect to a share exported by Windows 95, you should
-  say Y here. The Windows 95 server contains a bug that makes listing
-  directories unreliable. This option slows down the listing of
-  directories. This makes the Windows 95 server a bit more stable.
-
 Coda filesystem support
 CONFIG_CODA_FS
   Coda is an advanced network filesystem, similar to NFS in that it
@@ -11355,7 +11348,8 @@
 CONFIG_HOST_FOOTBRIDGE
   The 21285 Footbridge chip can operate in either `host mode' or
   `add-in' mode.  Say Y if your 21285 is in host mode, and therefore
-  is the configuration master, otherwise say N.
+  is the configuration master, otherwise say N.  This must not be
+  set to 'Y' if the card is used in 'add-in' mode.
 
 MFM harddisk support
 CONFIG_BLK_DEV_MFM
diff -u --recursive --new-file v2.3.6/linux/Documentation/pci.txt linux/Documentation/pci.txt
--- v2.3.6/linux/Documentation/pci.txt	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/pci.txt	Thu Jun 17 12:56:11 1999
@@ -4,7 +4,7 @@
 
 		"What should you avoid when writing PCI drivers"
 
-	  by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 13-Feb-1998
+	  by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 17-Jun-1999
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -18,6 +18,10 @@
 			configure_device(dev);
 
    For class-based search, use pci_find_class(CLASS_ID, dev).
+
+   You can use the constant PCI_ANY_ID as a wildcard replacement for
+VENDOR_ID or DEVICE_ID.  This allows searching for any device from a
+specific vendor, for example.
 
    In case you want to do some complex matching, look at pci_devices -- it's
 a linked list of pci_dev structures for all PCI devices in the system.
diff -u --recursive --new-file v2.3.6/linux/Makefile linux/Makefile
--- v2.3.6/linux/Makefile	Wed Jun  2 14:40:19 1999
+++ linux/Makefile	Wed Jun 16 19:26:27 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 6
+SUBLEVEL = 7
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.3.6/linux/arch/arm/Makefile linux/arch/arm/Makefile
--- v2.3.6/linux/arch/arm/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/Makefile	Thu Jun 17 01:11:35 1999
@@ -10,21 +10,31 @@
 # 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		:=
 
-# 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.
+# GCC 2.7 uses different options to later compilers; sort out which we have
+CONFIG_GCC_NEW		:= $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi)
+
+# Hack to get around RiscPC with StrongARM optimistaion
+# problem - force ARM710 optimisation for now.
+ifeq ($(CONFIG_GCC_NEW),y)
+  ifeq ($(CONFIG_ARCH_RPC),y)
+    ifeq ($(CONFIG_CPU_SA110),y)
+      CONFIG_CPU_SA110	:= n
+      CONFIG_CPU_ARM7	:= y
+    endif
+  endif
+endif
 
 ifeq ($(CONFIG_CPU_26),y)
   PROCESSOR		 = armo
   TEXTADDR		 = 0x02080000
   ZTEXTADDR		 = 0x01800000
   ZRELADDR		 = 0x02080000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
+  ifeq ($(CONFIG_GCC_NEW),y)
     CFLAGS_PROC		+= -mapcs-26 -mshort-load-bytes
     ifeq ($(CONFIG_CPU_ARM2),y)
       CFLAGS_PROC	+= -mcpu=arm2
@@ -49,7 +59,7 @@
 ifeq ($(CONFIG_CPU_32),y)
   PROCESSOR		 = armv
   TEXTADDR		 = 0xC0008000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
+  ifeq ($(CONFIG_GCC_NEW),y)
     CFLAGS_PROC		+= -mapcs-32 -mshort-load-bytes
     ifeq ($(CONFIG_CPU_ARM6),y)
       CFLAGS_PROC	+= -mcpu=arm6
@@ -68,10 +78,11 @@
 
 # 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   - 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).
 #
 
 COMPRESSED_HEAD	 = head.o
@@ -79,19 +90,16 @@
 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
@@ -103,13 +111,17 @@
 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
@@ -119,31 +131,13 @@
 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
+LD		 = $(CROSS_COMPILE)ld
 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=)
@@ -153,75 +147,40 @@
 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
+# If we're intending to debug the kernel, make sure it has line number
+# information.  This gets stripped out when building (z)Image so it doesn't
+# add anything to the footprint of the running kernel.
+ifeq ($(CONFIG_DEBUG_INFO),y)
+CFLAGS		+= -g
+endif
+
+HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o \
+		   arch/arm/kernel/init_task.o
+SUBDIRS		:= arch/arm/special $(SUBDIRS) arch/arm/lib arch/arm/kernel \
+		   arch/arm/mm 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)
-
-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
-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
+DRIVERS		+= arch/arm/special/special.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/block drivers/acorn/char drivers/acorn/net \
+		   drivers/acorn/scsi
+DRIVERS		+= drivers/acorn/block/acorn-block.a \
+		   drivers/acorn/char/acorn-char.a \
+		   drivers/acorn/net/acorn-net.a \
+		   drivers/acorn/scsi/acorn-scsi.a
 endif
 
-DRIVERS		:= $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS)
-
-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)
+ifeq ($(CONFIG_NWFPE),y)
+DRIVERS		+= arch/arm/nwfpe/math-emu.a
 endif
 
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
 symlinks::
 	$(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
-
 arch/arm/kernel: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
 
@@ -231,19 +190,20 @@
 arch/arm/lib: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/lib
 
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
-
-zImage: vmlinux
-	@$(MAKEBOOT) zImage
+zImage zinstall Image install: vmlinux
+	@$(MAKEBOOT) $@
 
-zinstall: vmlinux
-	@$(MAKEBOOT) zinstall
+# 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)/special mrproper
 
-Image: vmlinux
-	@$(MAKEBOOT) Image
+archclean:
+	@$(MAKEBOOT) clean
+	$(RM) arch/arm/lib/constants.h
 
-install: vmlinux
-	@$(MAKEBOOT) install
+archdep:
+	@$(MAKEBOOT) dep
 
 # My testing targets (that short circuit a few dependencies)
 zImg:;	@$(MAKEBOOT) zImage
@@ -251,10 +211,19 @@
 i:;	@$(MAKEBOOT) install
 zi:;	@$(MAKEBOOT) zinstall
 
-archclean:
-	@$(MAKEBOOT) clean
-	$(RM) arch/arm/lib/constants.h
+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 -u --recursive --new-file v2.3.6/linux/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile
--- v2.3.6/linux/arch/arm/boot/compressed/Makefile	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/boot/compressed/Makefile	Thu Jun 17 01:11:35 1999
@@ -11,10 +11,15 @@
 OBJS	=$(HEAD) misc.o $(COMPRESSED_EXTRA)
 CFLAGS	=-O2 -DSTDC_HEADERS $(CFLAGS_PROC)
 ARFLAGS =rc
+FONTC	=$(TOPDIR)/drivers/video/font_acorn_8x8.c
+
+ifeq ($(CONFIG_ARCH_ACORN),y)
+OBJS	+= ll_char_wr.o font.o
+endif
 
 all:		vmlinux
 
-vmlinux:	piggy.o $(OBJS)
+vmlinux:	$(OBJS) piggy.o
 		$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o
 
 $(HEAD): 	$(HEAD:.o=.S)
@@ -28,6 +33,9 @@
 		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;
+
+font.o:		$(FONTC)
+		$(CC) -Dstatic= -c -o $@ $(FONTC)
 
 clean:;		rm -f vmlinux core
 
diff -u --recursive --new-file v2.3.6/linux/arch/arm/boot/compressed/ll_char_wr.S linux/arch/arm/boot/compressed/ll_char_wr.S
--- v2.3.6/linux/arch/arm/boot/compressed/ll_char_wr.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/ll_char_wr.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.3.6/linux/arch/arm/config.in	Thu Jan 14 10:29:28 1999
+++ linux/arch/arm/config.in	Thu Jun 17 01:11:35 1999
@@ -14,18 +14,31 @@
 	 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
@@ -34,23 +47,19 @@
   define_bool CONFIG_ARCH_ACORN n
 fi
 
-if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
-  define_bool CONFIG_BUS_I2C y
-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
+     "$CONFIG_HOST_FOOTBRIDGE" = "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
+     "$CONFIG_ARCH_NETWINDER" = "y" ]; then
   define_bool CONFIG_ISA_DMA y
 else
   define_bool CONFIG_ISA_DMA n
@@ -59,7 +68,6 @@
 # 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
@@ -71,7 +79,6 @@
 
 # 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.
-
 choice 'Optimise for CPU'				\
 	"ARM2		CONFIG_CPU_ARM2 \
 	 ARM3		CONFIG_CPU_ARM3 \
@@ -80,22 +87,21 @@
 	 SA110		CONFIG_CPU_SA110" ARM6
 
 if [ "$CONFIG_CPU_26" = "y" ]; then
-
 # 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.
-
   choice 'Physical memory size'		\
 	"4MB+		CONFIG_PAGESIZE_32	\
-	 2MB		CONFIG_PAGESIZE_16	\
-	 1MB/512K	CONFIG_PAGESIZE_8" 4MB+
+	 2MB		CONFIG_PAGESIZE_16" 4MB+
 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 'Enable kernel-mode alignment trap handler (EXPERIMENTAL)' CONFIG_ALIGNMENT_TRAP
+fi
+bool 'Split text into discardable sections' CONFIG_TEXT_SECTIONS
 endmenu
 
 mainmenu_option next_comment
@@ -113,13 +119,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,13 +141,29 @@
     fi
   fi
 fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" -o \
-     "$CONFIG_ARCH_EBSA110" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
+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
 
+source drivers/i2o/Config.in
+
 source drivers/pnp/Config.in
 
 source drivers/block/Config.in
@@ -144,15 +172,19 @@
   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_MOUSE" = "y" ]; then
+    if [ "$CONFIG_ARCH_RPC" != "y" ]; then
+      define_bool CONFIG_KBDMOUSE y
+    else
+      define_bool CONFIG_RPCMOUSE y
+    fi
+  fi
 fi
 
+source drivers/usb/Config.in
+
 if [ "$CONFIG_VT" = "y" ]; then
   mainmenu_option next_comment
   comment 'Console drivers'
@@ -166,9 +198,11 @@
 
 if [ "$CONFIG_NET" = "y" ]; then
   source net/Config.in
-fi
 
-if [ "$CONFIG_NET" = "y" ]; then
+  source net/ax25/Config.in
+
+  source net/irda/Config.in
+
   mainmenu_option next_comment
   comment 'Network device support'
 
@@ -179,6 +213,15 @@
   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'
 
@@ -200,21 +243,29 @@
   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_EXPERIMENTAL" = "y" ]; then
+  if [ "$CONFIG_CPU_26" = "y" ]; then
+    bool 'Disable pgtable cache (EXPERIMENTAL)' CONFIG_NO_PGT_CACHE
+  fi
+
+  # 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 -u --recursive --new-file v2.3.6/linux/arch/arm/defconfig linux/arch/arm/defconfig
--- v2.3.6/linux/arch/arm/defconfig	Thu Feb 25 10:46:46 1999
+++ linux/arch/arm/defconfig	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile
--- v2.3.6/linux/arch/arm/kernel/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/Makefile	Thu Jun 17 01:11:35 1999
@@ -9,31 +9,37 @@
 ENTRY_OBJ = entry-$(PROCESSOR).o
 
 O_TARGET := kernel.o
-O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \
+O_OBJS   := $(ENTRY_OBJ) 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)
+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
 
+O_OBJS += $(O_OBJS_$(MACHINE))
+
 ifeq ($(CONFIG_MODULES),y)
   OX_OBJS = armksyms.o
-else
-  O_OBJS += armksyms.o
+endif
+
+ifeq ($(CONFIG_ARCH_ACORN),y)
+  OX_OBJS += ecard.o
 endif
 
 ifeq ($(MACHINE),nexuspci)
@@ -46,17 +52,23 @@
   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
+ifdef CONFIG_LEDS
+  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 $@
@@ -72,3 +84,7 @@
 
 lib:
 	$(MAKE) -C ../lib constants.h
+
+# Spell out some dependencies that `make dep' doesn't spot
+entry-armv.o: calls.S
+entry-armo.o: calls.S
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c
--- v2.3.6/linux/arch/arm/kernel/armksyms.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/armksyms.c	Thu Jun 17 01:11:35 1999
@@ -6,20 +6,40 @@
 #include <linux/mman.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/in6.h>
 
-#include <asm/ecard.h>
 #include <asm/elf.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <asm/pgtable.h>
+#include <asm/semaphore.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 
 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 void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
+extern void iounmap(void *addr);
+
+extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+
+/*
+ * 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_open(const char *, int, 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 +63,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 +79,46 @@
 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
+
 	/* 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);
+EXPORT_SYMBOL(local_bh_count);
+EXPORT_SYMBOL(local_irq_count);
+#ifdef CONFIG_CPU_32
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
 #endif
+EXPORT_SYMBOL(kernel_thread);
 
 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);
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
 
 	/* address translation */
 #ifndef __virt_to_phys__is_a_macro
@@ -98,7 +134,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);
 
@@ -167,3 +205,17 @@
 EXPORT_SYMBOL(armidlist);
 EXPORT_SYMBOL(armidindex);
 EXPORT_SYMBOL(elf_platform);
+
+	/* 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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/arthur.c linux/arch/arm/kernel/arthur.c
--- v2.3.6/linux/arch/arm/kernel/arthur.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/arthur.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/calls.S linux/arch/arm/kernel/calls.S
--- v2.3.6/linux/arch/arm/kernel/calls.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/calls.S	Thu Jun 17 01:11:35 1999
@@ -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)
@@ -119,7 +119,7 @@
 		.long	SYMBOL_NAME(sys_newlstat)
 		.long	SYMBOL_NAME(sys_newfstat)
 		.long	SYMBOL_NAME(sys_uname)
-/* 110 */	.long	SYMBOL_NAME(sys_iopl)
+/* 110 */	.long	SYMBOL_NAME(sys_ni_syscall)
 		.long	SYMBOL_NAME(sys_vhangup)
 		.long	SYMBOL_NAME(sys_idle)
 		.long	SYMBOL_NAME(sys_syscall)		/* call a syscall */
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dec21285.c linux/arch/arm/kernel/dec21285.c
--- v2.3.6/linux/arch/arm/kernel/dec21285.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dec21285.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-a5k.c linux/arch/arm/kernel/dma-a5k.c
--- v2.3.6/linux/arch/arm/kernel/dma-a5k.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-a5k.c	Thu Jun 17 01:11:35 1999
@@ -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);
 	}
 }
@@ -81,6 +84,11 @@
 		disable_irq(dma->dma_irq);
 		release_fiq(&fh);
 	}
+}
+
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
 }
 
 __initfunc(void arch_dma_init(dma_t *dma))
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-arc.c linux/arch/arm/kernel/dma-arc.c
--- v2.3.6/linux/arch/arm/kernel/dma-arc.c	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/kernel/dma-arc.c	Thu Jun 17 01:11:35 1999
@@ -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,7 +15,7 @@
 
 #include "dma.h"
 
-int arch_request_dma(dmach_t channel, dma_t *dma)
+int arch_request_dma(dmach_t channel, dma_t *dma, const char * dev_id)
 {
 	if (channel == DMA_VIRTUAL_FLOPPY0 ||
 	    channel == DMA_VIRTUAL_FLOPPY1)
@@ -25,16 +26,12 @@
 
 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)
 {
 	switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD
 	case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
 		switch (dma->dma_mode) {
 		case DMA_MODE_READ: /* read */
@@ -96,9 +93,38 @@
 		restore_flags(flags);
 	}
 	break;
+#endif
 	}
 }
 
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+  switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD
+    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 &&
@@ -106,6 +132,11 @@
 		printk("arch_disable_dma: invalid channel %d\n", channel);
 	else
 		disable_irq(dma->dma_irq);
+}
+
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
 }
 
 __initfunc(void arch_dma_init(dma_t *dma))
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-dummy.c linux/arch/arm/kernel/dma-dummy.c
--- v2.3.6/linux/arch/arm/kernel/dma-dummy.c	Fri May  8 00:42:38 1998
+++ linux/arch/arm/kernel/dma-dummy.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-ebsa285.c linux/arch/arm/kernel/dma-ebsa285.c
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-footbridge.c linux/arch/arm/kernel/dma-footbridge.c
--- v2.3.6/linux/arch/arm/kernel/dma-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/dma-footbridge.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,112 @@
+/*
+ * 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/malloc.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.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;
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+#ifdef CONFIG_ISA_DMA
+	has_isa_dma = isa_init_dma();
+#endif
+}
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-isa.c linux/arch/arm/kernel/dma-isa.c
--- v2.3.6/linux/arch/arm/kernel/dma-isa.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-isa.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-isa.h linux/arch/arm/kernel/dma-isa.h
--- v2.3.6/linux/arch/arm/kernel/dma-isa.h	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-isa.h	Thu Jun 17 01:11:35 1999
@@ -23,3 +23,7 @@
  */
 void isa_disable_dma(int channel, dma_t *dma);
 
+/*
+ * Initialise DMA
+ */
+int isa_init_dma(void);
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-rpc.c linux/arch/arm/kernel/dma-rpc.c
--- v2.3.6/linux/arch/arm/kernel/dma-rpc.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/dma-rpc.c	Thu Jun 17 01:11:35 1999
@@ -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,6 +319,46 @@
 	}
 }
 
+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);
+}
+
 __initfunc(void arch_dma_init(dma_t *dma))
 {
 	outb(0, IOMD_IO0CR);
@@ -326,7 +366,7 @@
 	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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-vnc.c linux/arch/arm/kernel/dma-vnc.c
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma.c linux/arch/arm/kernel/dma.c
--- v2.3.6/linux/arch/arm/kernel/dma.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma.c	Thu Jun 17 01:11:35 1999
@@ -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,6 +219,7 @@
 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))
 {
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma.h linux/arch/arm/kernel/dma.h
--- v2.3.6/linux/arch/arm/kernel/dma.h	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/kernel/dma.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ecard.c linux/arch/arm/kernel/ecard.c
--- v2.3.6/linux/arch/arm/kernel/ecard.c	Mon Dec 28 11:04:21 1998
+++ linux/arch/arm/kernel/ecard.c	Thu Jun 17 01:11:35 1999
@@ -7,32 +7,43 @@
  *
  * 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/proc_fs.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 +51,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 wait_queue_head_t ecard_wait;
+static wait_queue_head_t 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 +486,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 +501,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 +523,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 +539,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 +552,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 +568,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 +579,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 +676,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 +713,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 +732,80 @@
 	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 const char *unknown = "*unknown*";
+
 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,28 +813,57 @@
 		buffer += sprintf(buffer, "[%04X:%04X] ",
 			ec->cid.manufacturer, ec->cid.product);
 
-		if (!ec->card_desc && ec->cid.is && ec->cid.cd &&
-		    ecard_readchunk(&incd, ec, 0xf5, 0))
-			ec->card_desc = incd.d.string;
+		if (!ec->card_desc && ec->cid.cd &&
+		    ecard_readchunk(&incd, ec, 0xf5, 0)) {
+			ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL);
 
-		if (!ec->card_desc)
-			ec->card_desc = "*unknown*";
+			if (ec->card_desc)
+				strcpy(ec->card_desc, incd.d.string);
+		}
 
-		buffer += sprintf(buffer, "%s", ec->card_desc);
+		buffer += sprintf(buffer, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*");
 	} 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)
+int get_ecard_dev_info(char *buf, char **start, off_t pos, int count, int wr)
 {
-	return v[0] | v[1] << 8;
+	ecard_t *ec = cards;
+	off_t at = 0;
+	int len, cnt;
+
+	cnt = 0;
+	while (ec && count > cnt) {
+		len = ecard_prints(buf, ec);
+		at += len;
+		if (at >= pos) {
+			if (!*start) {
+				*start = buf + (pos - (at - len));
+				cnt = at - pos;
+			} else
+				cnt += len;
+			buf += len;
+		}
+		ec = ec->next;
+	}
+	return (count > cnt) ? cnt : count;
 }
 
-static inline signed long ecard_gets24(unsigned char *v)
+static struct proc_dir_entry proc_ecard_devices = {
+	PROC_BUS_ECARD_DEVICES, 7, "devices",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations,
+	get_ecard_dev_info
+};
+
+static struct proc_dir_entry *proc_bus_ecard_dir;
+
+static void ecard_proc_init(void)
 {
-	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+	proc_bus_ecard_dir = create_proc_entry("ecard", S_IFDIR, proc_bus);
+	proc_register(proc_bus_ecard_dir, &proc_ecard_devices);
 }
 
 /*
@@ -376,33 +872,39 @@
  * 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);
 
-	irqno_to_expcard[card] = -1;
+	if (!ec)
+		goto nodev;
 
-	ec->slot_no	= card;
+	memset(ec, 0, sizeof(ecard_t));
+
+	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;
-
-	irqno_to_expcard[card] = freeslot;
+		goto nodev;
 
-	ec->type	= type;
 	ec->cid.id	= cid.r_id;
 	ec->cid.cd	= cid.r_cd;
 	ec->cid.is	= cid.r_is;
@@ -415,9 +917,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 +932,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;
 
-	if (!cids) {
-		for (card = ecard_startcard; card < ecard_numcards; card++)
-			if (!expcard[card].claimed &&
-			    (expcard[card].cid.id ^ cid) == 0)
+	for (; finding_pos; finding_pos = finding_pos->next) {
+		if (finding_pos->claimed)
+			continue;
+
+		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 +1006,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;
+	for (ec = cards; ec; ec = ecn) {
+		ecn = ec->next;
 
-	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;
+		kfree(ec);
 	}
 
-	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;
-}
-
-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);
+	cards = NULL;
 
-#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;
-
-#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 +1031,38 @@
  */
 __initfunc(void ecard_init(void))
 {
-	int i, nc = 0;
+	int slot;
 
-	memset(expcard, 0, sizeof(expcard));
+	oldlatch_init();
 
-#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;
-	}
+#ifdef CONFIG_CPU_32
+	init_waitqueue_head(&ecard_wait);
+	init_waitqueue_head(&ecard_done);
 #endif
 
-	printk("Installed expansion cards:");
+	printk("Probing expansion cards: (does not imply support)\n");
 
-	/*
-	 * 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);
-
-	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();
+	ecard_proc_init();
 }
+
+EXPORT_SYMBOL(ecard_startfind);
+EXPORT_SYMBOL(ecard_find);
+EXPORT_SYMBOL(ecard_readchunk);
+EXPORT_SYMBOL(ecard_address);
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/entry-armo.S linux/arch/arm/kernel/entry-armo.S
--- v2.3.6/linux/arch/arm/kernel/entry-armo.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-armo.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S
--- v2.3.6/linux/arch/arm/kernel/entry-armv.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-armv.S	Thu Jun 17 01:11:35 1999
@@ -61,8 +61,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 +190,109 @@
 		.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_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 +306,22 @@
 		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
+		mrs	r1, cpsr			@ disable IRQs
+		orr	r1, r1, #I_BIT
 		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
+		msr	cpsr, r1
 		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
 
@@ -348,25 +348,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,231 +360,89 @@
 		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
-
-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
 		tst	r3, #I_BIT
 		mrseq	r0, cpsr			@ Enable interrupts if they were
@@ -619,29 +458,15 @@
 		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 +474,414 @@
 		@
 		adrsvc	ne, lr, 1b
 		bne	do_IRQ
-		b	ret_with_reschedule
-
-		irq_prio_table
+		ldr	r0, [sp, #S_PSR]
+		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:		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
+ */
+#ifdef DEBUG_UNDEF
+t:		.ascii "Prefetch -> undefined instruction\n\0"
+		.align
+#endif
+		.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}^
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)]
+		mcr	p15, 0, r7, c1, c0
+#endif
+
+		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
+		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}^
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, [r4, #OFF_CR_ALIGNMENT(__temp_irq)]
+		mcr	p15, 0, r7, c1, c0
+#endif
+
+		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
-		mov	fp, #0
+		stmdb	r8, {sp, lr}^			@ Save user r0 - r12
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, [r4, #OFF_CR_ALIGNMENT(__temp_und)]
+		mcr	p15, 0, r7, c1, c0
+#endif
 
-		adrsvc	al, r9, ret_from_exception	@ r9  = normal FP return
+		mov	fp, #0
+		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
-
-		mov	r0, r5				@ unsigned long pc
-		mov	r1, sp				@ struct pt_regs *regs
-		bl	SYMBOL_NAME(do_undefinstr)
-
-3:		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
-		msr	spsr, lr
-		ldmia	sp, {r0 - pc}^			@ Restore SVC 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
+		adrsvc	al, lr, ret_from_sys_call
 		b	SYMBOL_NAME(do_undefinstr)
 
-__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...
+		.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
 
-/* 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
- */
-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
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)]
+		mcr	p15, 0, r7, c1, c0
+#endif
 
-wfs_mask_data:	.word	0x0e200110			@ WFS
-		.word	0x0fff0fff
-		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
-		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
-		.word	0x0f0f0f00
+		mov	fp, #0
+		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_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)
+		ldr	lr, [sp, #S_PSR]		@ Get USR cpsr
+		msr	spsr, lr
+		ldmia	sp, {r0 - pc}^			@ Restore USR registers
 
 #include "entry-common.S"
 
+		.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.
+ */
+		.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
+		@
+		bic	r13, lr, #63
+		orr	r13, r13, #0x93
+		msr	spsr, r13			@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		adr	r13, .LCtab_irq
+		ldr	lr, [r13, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+/*
+ * 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
+		@
+		bic	r13, lr, #63
+		orr	r13, r13, #0x93
+		msr	spsr, r13			@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		adr	r13, .LCtab_dabt
+		ldr	lr, [r13, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+/*
+ * 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
+		@
+		bic	r13, lr, #63
+		orr	r13, r13, #0x93
+		msr	spsr, r13			@ switch to SVC_32 mode
+
+		ands	lr, lr, #15
+		ldreq	lr, .LCtab_pabt
+		ldrne	lr, .LCtab_pabt + 4
+		movs	pc, lr
+
+/*
+ * 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
+		@
+		bic	r13, lr, #63
+		orr	r13, r13, #0x93
+		msr	spsr, r13			@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		adr	r13, .LCtab_und
+		ldr	lr, [r13, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+/*=============================================================================
+ * 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
+
+.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
+
+.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
+
+.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
+
+.LCtab_pabt:	.word	__pabt_usr
+		.word	__pabt_invalid
+
+.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 +891,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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S
--- v2.3.6/linux/arch/arm/kernel/entry-common.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-common.S	Thu Jun 17 01:11:35 1999
@@ -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,25 @@
 /*============================================================================
  * 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
+		ldmleib	sp, {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 +190,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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/fiq.c linux/arch/arm/kernel/fiq.c
--- v2.3.6/linux/arch/arm/kernel/fiq.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/fiq.c	Thu Jun 17 01:11:35 1999
@@ -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
@@ -78,7 +80,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 +108,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)
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/head-armv.S linux/arch/arm/kernel/head-armv.S
--- v2.3.6/linux/arch/arm/kernel/head-armv.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/head-armv.S	Thu Jun 17 01:11:35 1999
@@ -7,13 +7,21 @@
  */
 #include <linux/config.h>
 #include <linux/linkage.h>
+#include <asm/hardware.h>
+#include <asm/dec21285.h>
+
+		.globl	SYMBOL_NAME(swapper_pg_dir)
+		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x4000
+
+		.section ".text.init",#alloc,#execinstr
+ENTRY(stext)
+ENTRY(_stext)
 
-#ifndef CONFIG_ARCH_VNC
 #if (TEXTADDR & 0xffff) != 0x8000
 #error TEXTADDR must start at 0xXXXX8000
 #endif
-#else
-		.text
+
+#ifdef CONFIG_ARCH_NETWINDER
 		mov	r0, r0
 		mov	r0, r0
 		mov	r0, r0
@@ -22,16 +30,34 @@
 		mov	r0, r0
 		mov	r0, r0
 		mov	r0, r0
+
+		adr	r2, 1f
+		ldmdb	r2, {r7, r8}
+		and	r3, r2, #0x0000c000
+		teq	r3, #0x00008000
+		beq	__entry
+		bic	r3, r2, #0xc000
+		orr	r3, r3, #0x8000
+		mov	r0, r3
+		mov	r4, #32
+		sub	r5, r8, r7
+		b	1f
+
+		.word	_stext
+		.word	_end
+
+1:		ldmia	r2!, {r6, r7, r8, r9}
+		stmia	r3!, {r6, r7, r8, r9}
+		subs	r4, r4, #16
+		bcs	1b
+		movs	r4, r5
+		mov	r5, #0
+		movne	pc, r0
+
 		mov	r0, #0
 		mov	r1, #5
 #endif
 
-#define DEBUG
-
-		.globl	SYMBOL_NAME(swapper_pg_dir)
-		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x4000
-
-		.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
@@ -45,16 +71,15 @@
  *  r1 = 5 -> Corel Netwinder
  *  r1 = 6 -> CATS
  *  r1 = 7 -> tbox
+ *  r1 = 8 -> SA110/21285 as co-processor
  */
 
-ENTRY(stext)
-ENTRY(_stext)
 __entry:	teq	r0, #0					@ check for illegal entry...
 		bne	.Lerror					@ loop indefinitely
-		cmp	r1, #8					@ Unknown machine architecture
+		cmp	r1, #9					@ 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...
+/* 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
@@ -74,7 +99,7 @@
 
 		adr	r4, .LCMachTypes
 		add	r4, r4, r1, lsl #4
-		ldmia	r4, {r4, r5, r6}
+		ldmia	r4, {r4, r5, r6, r7}
 /*
  * r4 = page dir in physical ram
  * r5 = physical address of start of RAM
@@ -99,26 +124,28 @@
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
-#ifdef DEBUG
+#ifdef CONFIG_DEBUG_LL
 /* Map in IO space
  * This allows debug messages to be output via a serial
  * before/while paging_init.
  */
-		add	r0, r4, #0x3800
+		add	r0, r4, r7
 		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
@@ -168,49 +195,55 @@
 .LCMachTypes:	.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000	@ Address of page tables (physical)
 		.long	0					@ Address of RAM
 		.long	0xe0000000				@ I/O address
-		.long	0
+		.long	0x3800
 
 		@ Acorn RiscPC
 		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000
 		.long	0x10000000
 		.long	0x03000000
-		.long	0
+		.long	0x3800
 
 		@ EBSIT ???
 		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000
 		.long	0
 		.long	0xe0000000
-		.long	0
+		.long	0x3800
 
 		@ NexusPCI
 		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000
 		.long	0x40000000
 		.long	0x10000000
-		.long	0
+		.long	0x3800
 
 		@ 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)
-		.long	0
+		.long	0x3800
 
 		@ 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	0x3800
 
 		@ 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)
-		.long	0
+		.long	0x3800
 
 		@ tbox
 		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000
 		.long	0x80000000				@ Address of RAM
 		.long	0x00400000				@ Uart
-		.long	0
+		.long	0x3800
+
+		@ DEC EBSA285 as co-processor
+		.long	0x4000					@ Address of page tables (physical)
+		.long	0					@ Address of RAM
+		.long	DC21285_ARMCSR_BASE			@ Physical I/O base address
+		.long	0x7cf00000 >> 18			@ Virtual I/O base address
 
 .LCProcTypes:	@ ARM6 / 610
 		.long	0x41560600
@@ -250,7 +283,11 @@
 		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
+#ifdef CONFIG_ALIGNMENT_TRAP
+		mov	r0, #0x3f				@ ....S..DPWCAM
+#else
 		mov	r0, #0x3d				@ ....S..DPWC.M
+#endif
 		orr	r0, r0, #0x100
 		mov	pc, lr
 
@@ -261,7 +298,11 @@
 		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
+#ifdef CONFIG_ALIGNMENT_TRAP
+		mov	r0, #0x7f				@ ....S.LDPWCAM
+#else
 		mov	r0, #0x7d				@ ....S.LDPWC.M
+#endif
 		orr	r0, r0, #0x100
 		mov	pc, lr
 
@@ -276,32 +317,38 @@
 		mrc	p15, 0, r0, c1, c0			@ get control register v4
 		bic	r0, r0, #0x0e00
 		bic	r0, r0, #0x0002
+#ifdef CONFIG_ALIGNMENT_TRAP
+		orr	r0, r0, #0x003f				@ I...S..DPWCAM
+#else
 		orr	r0, r0, #0x003d				@ I...S..DPWC.M
+#endif
 		orr	r0, r0, #0x1100				@ v4 supports separate I cache
 		mov	pc, lr
 
-		.section ".text.init",#alloc,#execinstr
-
 .Lsa_fastclock:	mcr	p15, 0, r4, c15, c1, 2			@ Enable clock switching
 		mov	pc, lr
 
 .LC0:		.long	SYMBOL_NAME(__entry)
-		.long	SYMBOL_NAME(machine_type)
+		.long	SYMBOL_NAME(__machine_arch_type)
 		.long	SYMBOL_NAME(__bss_start)
 		.long	SYMBOL_NAME(processor_id)
 		.long	SYMBOL_NAME(_end)
+		.long	SYMBOL_NAME(cr_alignment)
 		.long	SYMBOL_NAME(init_task_union)+8192
 		.align
 
 .Lalready_done_mmap:
 		adr	r4, .LC0
-		ldmia	r4, {r3, r4, r5, r6, r8, sp}		@ Setup stack
+		ldmia	r4, {r3, r4, r5, r6, r7, r8, sp}	@ Setup stack
 		add	r10, r10, r3				@ Add base back in
 		mov	fp, #0
-1:		cmp	r5, r8					@ Clear BSS
+1:		cmp	r5, r7					@ Clear BSS
 		strcc	fp, [r5],#4
 		bcc	1b
 
+		bic	r2, r0, #2				@ Clear 'A' bit
+		stmia	r8, {r0, r2}				@ Save control register values
+
 		str	r1, [r4]				@ Save machine type
 		str	r9, [r6]				@ Save processor ID
 		mov	lr, pc
@@ -310,10 +357,12 @@
 		b	SYMBOL_NAME(start_kernel)
 
 		.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 +411,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 +531,6 @@
 		mov	r1, r0
 		mov	r0, #0
 		b	1b
-
-		.ltorg
 
 		.bss
 hexbuf:		.space 16
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/hw-ebsa285.c linux/arch/arm/kernel/hw-ebsa285.c
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/hw-footbridge.c linux/arch/arm/kernel/hw-footbridge.c
--- v2.3.6/linux/arch/arm/kernel/hw-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/hw-footbridge.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,893 @@
+/*
+ * 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
+
+__initfunc(void hw_init(void))
+{
+#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
+
+	leds_event(led_start);
+}
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/iic.c linux/arch/arm/kernel/iic.c
--- v2.3.6/linux/arch/arm/kernel/iic.c	Sat Jul 18 11:55:23 1998
+++ linux/arch/arm/kernel/iic.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/init_task.c linux/arch/arm/kernel/init_task.c
--- v2.3.6/linux/arch/arm/kernel/init_task.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/init_task.c	Thu Jun 17 01:11:35 1999
@@ -6,9 +6,10 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
+struct mm_struct init_mm = INIT_MM(init_mm);
 
 /*
  * Initial task structure.
@@ -20,4 +21,5 @@
  *
  * The things we do for performance..
  */
-union task_union init_task_union __attribute__((__section__(".init.task"))) = { INIT_TASK };
+union task_union init_task_union __attribute__((__section__(".init.task"))) =
+		{ INIT_TASK(init_task_union.task) };
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ioport.c linux/arch/arm/kernel/ioport.c
--- v2.3.6/linux/arch/arm/kernel/ioport.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/ioport.c	Wed Dec 31 16:00:00 1969
@@ -1,29 +0,0 @@
-/*
- * linux/arch/arm/kernel/ioport.c
- *
- * Io-port support is not used for ARM
- */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/ioport.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)
-{
-}*/
-
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
-{
-	return -ENOSYS;
-}
-
-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)
-{
-	return -ENOSYS;
-}
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
--- v2.3.6/linux/arch/arm/kernel/irq.c	Wed Dec 23 09:44:40 1998
+++ linux/arch/arm/kernel/irq.c	Thu Jun 17 01:11:35 1999
@@ -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>
-
 __initfunc(void init_IRQ(void))
 {
 	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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-ebsa110.c linux/arch/arm/kernel/leds-ebsa110.c
--- v2.3.6/linux/arch/arm/kernel/leds-ebsa110.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/leds-ebsa110.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-ebsa285.c linux/arch/arm/kernel/leds-ebsa285.c
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-footbridge.c linux/arch/arm/kernel/leds-footbridge.c
--- v2.3.6/linux/arch/arm/kernel/leds-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/leds-footbridge.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,249 @@
+/*
+ * 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/hardware.h>
+#include <asm/leds.h>
+#include <asm/spinlock.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;
+
+#ifdef CONFIG_ARCH_EBSA285
+
+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 = 0;
+		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)
+{
+}
+
+__initfunc(void
+init_leds_event(led_event_t evt))
+{
+	switch (machine_arch_type) {
+#ifdef CONFIG_ARCH_EBSA285
+	case MACH_TYPE_EBSA285:
+		leds_event = ebsa285_leds_event;
+		break;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+	case MACH_TYPE_NETWINDER:
+		leds_event = netwinder_leds_event;
+		break;
+#endif
+
+	default:
+		leds_event = dummy_leds_event;
+	}
+
+	leds_event(evt);
+}
+
+void (*leds_event)(led_event_t) = init_leds_event;
+
+EXPORT_SYMBOL(leds_event);
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/oldlatches.c linux/arch/arm/kernel/oldlatches.c
--- v2.3.6/linux/arch/arm/kernel/oldlatches.c	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/oldlatches.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/process.c linux/arch/arm/kernel/process.c
--- v2.3.6/linux/arch/arm/kernel/process.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/process.c	Thu Jun 17 01:11:35 1999
@@ -34,7 +34,6 @@
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
@@ -55,46 +54,37 @@
 }
 
 /*
- * The idle loop on an arm..
+ * The idle loop on an ARM...
  */
 asmlinkage int sys_idle(void)
 {
-	int ret = -EPERM;
-
-	lock_kernel();
 	if (current->pid != 0)
-		goto out;
+		return -EPERM;
+
 	/* endless idle loop with no priority at all */
-	current->priority = -100;
-	for (;;)
-	{
+	while (1) {
+		if (!current->need_resched && !hlt_counter)
+			proc_idle();
+		current->policy = SCHED_YIELD;
+		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 ();
+	arch_reset(reboot_mode);
+	panic("Reboot failed\n");
+	while (1);
 }
 
 void machine_halt(void)
@@ -150,6 +140,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)
@@ -179,9 +230,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;
@@ -224,3 +276,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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
--- v2.3.6/linux/arch/arm/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
+++ linux/arch/arm/kernel/ptrace.c	Thu Jun 17 01:11:35 1999
@@ -34,11 +34,11 @@
  */
 static inline long get_stack_long(struct task_struct *task, int offset)
 {
-	unsigned char *stack;
+	struct pt_regs *regs;
 
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	return *(unsigned long *)stack;
+	regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
+
+	return regs->uregs[offset];
 }
 
 /*
@@ -50,11 +50,12 @@
 static inline long put_stack_long(struct task_struct *task, int offset,
 	unsigned long data)
 {
-	unsigned char *stack;
+	struct pt_regs *regs;
+
+	regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
+
+	regs->uregs[offset] = data;
 
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	*(unsigned long *) stack = data;
 	return 0;
 }
 
@@ -157,11 +158,16 @@
 	
 	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();
 }
 
 /*
@@ -343,8 +349,7 @@
 printk ("=%08lX ", val);
 	return val;
 }
-#undef pc_pointer
-#define pc_pointer(x) ((x) & 0x03fffffc)
+
 int ptrace_set_bpt (struct task_struct *child)
 {
 	unsigned long insn, pc, alt;
@@ -651,7 +656,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;
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c
--- v2.3.6/linux/arch/arm/kernel/setup.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/setup.c	Thu Jun 17 01:11:35 1999
@@ -56,12 +56,17 @@
 #define SUPPORT_CPU_SA110
 #endif
 
-#ifndef CONFIG_CMDLINE
-#define CONFIG_CMDLINE	"root=/dev/nfs rw"
-#endif
 #define MEM_SIZE	(16*1024*1024)
 #define COMMAND_LINE_SIZE 256
 
+#ifndef CONFIG_CMDLINE
+#define CONFIG_CMDLINE ""
+#endif
+
+extern void reboot_setup(char *str, int *ints);
+extern void fpe_init(void);
+extern void disable_hlt(void);
+
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info = {
  orig_video_lines:	30,
@@ -87,20 +92,26 @@
   /*-- Match -- --- Mask -- -- Manu --  Processor  uname -m   --- ELF STUFF ---
 	--- processor asm funcs --- */
 #if defined(CONFIG_CPU_26)
+  /* ARM2 fake ident */
   { 0x41560200, 0xfffffff0, "ARM/VLSI",	"arm2"	 , "armv1"  , "v1", 0,
 	&arm2_processor_functions   },
+  /* ARM250 fake ident */
   { 0x41560250, 0xfffffff0, "ARM/VLSI",	"arm250" , "armv2"  , "v2", HWCAP_SWP,
 	&arm250_processor_functions },
+  /* ARM3 processors */
   { 0x41560300, 0xfffffff0, "ARM/VLSI",	"arm3"	 , "armv2"  , "v2", HWCAP_SWP,
 	&arm3_processor_functions   },
 #elif defined(CONFIG_CPU_32)
 #ifdef SUPPORT_CPU_ARM6
+  /* ARM6 */
   { 0x41560600, 0xfffffff0, "ARM/VLSI",	"arm6"	 , "armv3"  , "v3", HWCAP_SWP,
 	&arm6_processor_functions   },
+  /* ARM610 */
   { 0x41560610, 0xfffffff0, "ARM/VLSI",	"arm610" , "armv3"  , "v3", HWCAP_SWP,
 	&arm6_processor_functions   },
 #endif
 #ifdef SUPPORT_CPU_ARM7
+  /* ARM7's have a strange numbering */
   { 0x41007000, 0xffffff00, "ARM/VLSI",	"arm7"	 , "armv3"  , "v3", HWCAP_SWP,
 	&arm7_processor_functions   },
   /* ARM710 IDs are non-standard */
@@ -108,10 +119,16 @@
 	&arm7_processor_functions   },
 #endif
 #ifdef SUPPORT_CPU_SA110
-  { 0x4401a100, 0xfffffff0, "DEC",	"sa110"	 , "armv4"  , "v3", HWCAP_SWP|HWCAP_HALF,
+#ifdef CONFIG_ARCH_RPC
+  /* Acorn RiscPC's can't handle ARMv4 half-word instructions */
+  { 0x4401a100, 0xfffffff0, "Intel",	"sa110"	 , "armv4"  , "v4", HWCAP_SWP,
+	&sa110_processor_functions  },
+#else
+  { 0x4401a100, 0xfffffff0, "Intel",	"sa110"	 , "armv4"  , "v4", HWCAP_SWP|HWCAP_HALF,
 	&sa110_processor_functions  },
 #endif
 #endif
+#endif
   { 0x00000000, 0x00000000, "***", "unknown", "unknown", "**", 0, NULL }
 };
 
@@ -119,7 +136,7 @@
  * From head-armv.S
  */
 unsigned int processor_id;
-unsigned int machine_type;
+unsigned int __machine_arch_type;
 int armidindex;
 
 extern int root_mountflags;
@@ -132,139 +149,10 @@
  */
 
 /*
- * Risc-PC specific initialisation
- */
-#ifdef CONFIG_ARCH_RPC
-
-#include <asm/arch/mmu.h>
-
-unsigned int vram_half_sam;
-
-static void
-setup_rpc(struct param_struct *params)
-{
-	extern void init_dram_banks(const struct param_struct *params);
-
-	init_dram_banks(params);
-
-	switch (params->u1.s.pages_in_vram) {
-	case 256:
-		vram_half_sam = 1024;
-		break;
-	case 512:
-	default:
-		vram_half_sam = 2048;
-	}
-}
-#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))
-{
-	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;
-
-#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;
-
-	setup_rpc(params);
-
-	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;
-	}
-#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
-
-	return params->commandline;
-}
-
-#else
-
-static char default_command_line[] __initdata = CONFIG_CMDLINE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
-{
-	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
-
-	*mem_end_p = PAGE_OFFSET + MEM_SIZE;
-
-	return default_command_line;
-}
-#endif
-
-/*
  * initial ram disk
  */
 #ifdef CONFIG_BLK_DEV_INITRD
 __initfunc(static void
-setup_initrd(const struct param_struct *params))
-{
-	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;
-	}
-}
-
-__initfunc(static void
 check_initrd(unsigned long mem_start, unsigned long mem_end))
 {
 	if (initrd_end > mem_end) {
@@ -276,7 +164,6 @@
 }
 
 #else
-#define setup_initrd(p)
 #define check_initrd(ms,me)
 #endif
 
@@ -289,48 +176,47 @@
 	       armidlist[armidindex].mask)
 		armidindex += 1;
 
-	if (armidlist[armidindex].id == 0) {
-#ifdef CONFIG_ARCH_ACORN
-		int i;
-
-		for (i = 0; i < 3200; i++)
-			((unsigned long *)SCREEN2_BASE)[i] = 0x77113322;
-#endif
+	if (armidlist[armidindex].id == 0)
 		while (1);
-	}
 
 	processor = *armidlist[armidindex].proc;
 	processor._proc_init();
 }
 
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
 static char command_line[COMMAND_LINE_SIZE] = { 0, };
        char saved_command_line[COMMAND_LINE_SIZE];
 
 __initfunc(static void
-setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end))
+setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_sz))
 {
-	char c, *to = command_line;
+	char c = ' ', *to = command_line;
 	int len = 0;
 
 	*mem_start = (unsigned long)&_end;
 
 	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;
+		if (c == ' ') {
+			if (cmd_line[0] == 'm' &&
+			    cmd_line[1] == 'e' &&
+			    cmd_line[2] == 'm' &&
+			    cmd_line[3] == '=') {
+				*mem_sz = simple_strtoul(cmd_line+4, &cmd_line, 0);
+				switch(*cmd_line) {
+				case 'M':
+				case 'm':
+					*mem_sz <<= 10;
+				case 'K':
+				case 'k':
+					*mem_sz <<= 10;
+					cmd_line++;
+				}
+			}
+			/* if there are two spaces, remove one */
+			if (*cmd_line == ' ') {
 				cmd_line++;
+				continue;
 			}
-			*mem_end = *mem_end + PAGE_OFFSET;
 		}
 		c = *cmd_line++;
 		if (!c)
@@ -341,42 +227,222 @@
 	}
 
 	*to = '\0';
+
+	/* remove trailing spaces */
+	while (*--to == ' ' && to != command_line)
+		*to = '\0';
 }
 
+__initfunc(static void
+setup_ram(int doload, int prompt, int image_start))
+{
+#ifdef CONFIG_BLK_DEV_RAM
+	extern int rd_doload;
+	extern int rd_prompt;
+	extern int rd_image_start;
+
+	rd_image_start = image_start;
+	rd_prompt = prompt;
+	rd_doload = doload;
+#endif
+}
+
+/*
+ * initial ram disk
+ */
+__initfunc(static void
+setup_initrd(unsigned int start, unsigned int size))
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (start) {
+		initrd_start = start;
+		initrd_end   = start + size;
+	} else {
+		initrd_start = 0;
+		initrd_end   = 0;
+	}
+#endif
+}
+
+#ifdef CONFIG_ARCH_ACORN
+int memc_ctrl_reg;
+int number_mfm_drives;
+unsigned int vram_size;
+#endif
+
+#ifndef PARAMS_BASE
+#define PARAMS_BASE NULL
+#endif
+
+static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
+#define ENDIANNESS ((char)endian_test.l)
+
 __initfunc(void
 setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
+	struct param_struct *params = (struct param_struct *)PARAMS_BASE;
 	static unsigned char smptrap;
-	unsigned long memory_end;
-	char endian = 'l';
-	char *from;
+	unsigned long memory_end = 0;
+	char *from = NULL;
 
 	if (smptrap == 1)
 		return;
 	smptrap = 1;
 
+#if defined(CONFIG_ARCH_ARC)
+	__machine_arch_type = MACH_TYPE_ARCHIMEDES;
+#elif defined(CONFIG_ARCH_A5K)
+	__machine_arch_type = MACH_TYPE_A5K;
+#endif
+
 	setup_processor();
 
-	from = setup_params(&memory_end);
-	setup_initrd(params);
+	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;
+
+	/*
+	 * Add your machine dependencies here
+	 */
+	switch (machine_arch_type) {
+	case MACH_TYPE_EBSA110:
+		/* EBSA110 locks if we execute 'wait for interrupt' */
+		disable_hlt();
+		params = NULL;
+		break;
+
+	case MACH_TYPE_EBSA285:
+		if (params) {
+			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;
+		}
+		break;
+
+	case MACH_TYPE_CO285:
+		{
+#if 0
+			extern unsigned long boot_memory_end;
+			extern char boot_command_line[];
+
+			from = boot_command_line;
+			memory_end = boot_memory_end;
+#endif
+			params = NULL;
+		}
+		break;
+
+	case MACH_TYPE_CATS:
+		/* CATS must use soft-reboot */
+		reboot_setup("s", NULL);
+		break;
+
+	case MACH_TYPE_NETWINDER:
+		/*
+		 * to be fixed in a future NeTTrom
+		 */
+		if (params->u1.s.page_size == 4096) {
+			if (params->u1.s.nr_pages != 0x2000 &&
+			    params->u1.s.nr_pages != 0x4000) {
+				printk("Warning: bad NeTTrom parameters detected, using defaults\n");
+			    	/*
+			    	 * This stuff doesn't appear to be initialised
+			    	 * properly by NeTTrom 2.0.6 and 2.0.7
+			    	 */
+				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;
+				params->u1.s.video_x = 0;
+				params->u1.s.video_y = 0;
+				params->u1.s.video_num_cols = 80;
+				params->u1.s.video_num_rows = 30;
+			}
+		} else {
+			printk("Warning: no NeTTrom parameter page detected, using "
+			       "compiled-in settings\n");
+			params = NULL;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (params) {
+		memory_end	  = params->u1.s.page_size *
+				    params->u1.s.nr_pages;
+
+		ROOT_DEV	  = to_kdev_t(params->u1.s.rootdev);
+
+		setup_ram((params->u1.s.flags & FLAG_RDLOAD) == 0,
+			  (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+			  params->u1.s.rd_start);
+
+		setup_initrd(params->u1.s.initrd_start,
+			     params->u1.s.initrd_size);
+
+		if (!(params->u1.s.flags & FLAG_READONLY))
+			root_mountflags &= ~MS_RDONLY;
+
+#ifdef CONFIG_ARCH_ACORN
+#ifdef CONFIG_ARCH_RPC
+		{
+			extern void init_dram_banks(struct param_struct *);
+			init_dram_banks(params);
+		}
+#endif
+
+		memc_ctrl_reg	  = params->u1.s.memc_control_reg;
+		number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+		vram_size	  = 0;
+
+		switch (params->u1.s.pages_in_vram) {
+		case 512:
+			vram_size += PAGE_SIZE * 256;
+		case 256:
+			vram_size += PAGE_SIZE * 256;
+		default:
+			break;
+		}
+
+		memory_end -= vram_size;
+#endif
+
+		from = params->commandline;
+	} else {
+		ROOT_DEV	  = 0x00ff;
+
+		setup_ram(1, 1, 0);
+		setup_initrd(0, 0);
+	}
+
+	if (!memory_end)
+		memory_end = MEM_SIZE;
+
+	if (!from)
+		from = default_command_line;
+
+#ifdef CONFIG_NWFPE
+	fpe_init();
+#endif
 
 	/* Save unparsed command line copy for /proc/cmdline */
 	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
 	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
 
 	setup_mem(from, memory_start_p, &memory_end);
-	check_initrd(*memory_start_p, memory_end);
 
-	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;
+	memory_end += PAGE_OFFSET;
 
-	*cmdline_p = command_line;
-	*memory_end_p = memory_end;
+	check_initrd(*memory_start_p, memory_end);
 
-	sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, endian);
-	sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, endian);
+	sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, ENDIANNESS);
+	sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, ENDIANNESS);
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
@@ -385,43 +451,26 @@
 	conswitchp = &dummy_con;
 #endif
 #endif
+
+	*cmdline_p = command_line;
+	*memory_end_p = memory_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"		}
+static const char *machine_desc[] = {
+	"EBSA110",
+	"Acorn-RiscPC",
+	"unknown",
+	"Nexus-FTV/PCI",
+	"EBSA285",
+	"Corel-NetWinder",
+	"Chalice-CATS",
+	"unknown-TBOX",
+	"co-EBSA285",
+	"CL-PS7110",
+	"Acorn-Archimedes",
+	"Acorn-A5000"
 };
 
-#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
-
-#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
-
 int get_cpuinfo(char * buffer)
 {
 	int len;
@@ -429,25 +478,12 @@
 	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",
+		"Hardware\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
-		);
+		machine_desc[machine_arch_type]);
 	return len;
 }
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
--- v2.3.6/linux/arch/arm/kernel/signal.c	Thu Oct  8 09:32:22 1998
+++ linux/arch/arm/kernel/signal.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/sys_arm.c linux/arch/arm/kernel/sys_arm.c
--- v2.3.6/linux/arch/arm/kernel/sys_arm.c	Sat May  8 11:14:01 1999
+++ linux/arch/arm/kernel/sys_arm.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c
--- v2.3.6/linux/arch/arm/kernel/time.c	Thu Mar 11 23:24:55 1999
+++ linux/arch/arm/kernel/time.c	Thu Jun 17 01:11:35 1999
@@ -129,27 +129,12 @@
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti ();
+	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 ();
-}
-
-static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL};
-
 __initfunc(void time_init(void))
 {
-	xtime.tv_sec = setup_timer();
 	xtime.tv_usec = 0;
 
-	setup_arm_irq(IRQ_TIMER, &irqtimer);
+	setup_timer();
 }
diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c
--- v2.3.6/linux/arch/arm/kernel/traps.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/traps.c	Thu Jun 17 01:11:35 1999
@@ -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,138 @@
 		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);	
+}
 
-	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 +272,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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/Makefile linux/arch/arm/lib/Makefile
--- v2.3.6/linux/arch/arm/lib/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/lib/Makefile	Thu Jun 17 01:11:35 1999
@@ -6,14 +6,14 @@
 
 L_TARGET := lib.a
 L_OBJS   := backtrace.o bitops.o checksum.o delay.o io.o memcpy.o \
-	    system.o string.o uaccess.o
+	    semaphore.o string.o system.o uaccess.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 += loaders.o io-acorn.o
   ifdef CONFIG_ARCH_A5K
     L_OBJS += floppydma.o
   endif
@@ -26,12 +26,8 @@
   L_OBJS += io-ebsa110.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
@@ -45,10 +41,4 @@
 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
diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/checksum.S linux/arch/arm/lib/checksum.S
--- v2.3.6/linux/arch/arm/lib/checksum.S	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/checksum.S	Thu Jun 17 01:11:35 1999
@@ -520,13 +520,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 +538,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 +600,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 +652,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 +704,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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/floppydma.S linux/arch/arm/lib/floppydma.S
--- v2.3.6/linux/arch/arm/lib/floppydma.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/floppydma.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/getconsdata.c linux/arch/arm/lib/getconsdata.c
--- v2.3.6/linux/arch/arm/lib/getconsdata.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/getconsdata.c	Thu Jun 17 01:11:35 1999
@@ -67,6 +67,23 @@
 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 KSWI_BASE = 0x900000;
 unsigned long KSWI_SYS_BASE = 0x9f0000;
 unsigned long SYS_ERROR0 = 0x9f0000;
diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-acorn.S linux/arch/arm/lib/io-acorn.S
--- v2.3.6/linux/arch/arm/lib/io-acorn.S	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/io-acorn.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-ebsa110.S linux/arch/arm/lib/io-ebsa110.S
--- v2.3.6/linux/arch/arm/lib/io-ebsa110.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/io-ebsa110.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-ebsa285.S linux/arch/arm/lib/io-ebsa285.S
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-footbridge.S linux/arch/arm/lib/io-footbridge.S
--- v2.3.6/linux/arch/arm/lib/io-footbridge.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/io-footbridge.S	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,200 @@
+#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
+		add	\rd, \rd, #pcio_low
+		.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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io.c linux/arch/arm/lib/io.c
--- v2.3.6/linux/arch/arm/lib/io.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/io.c	Thu Jun 17 01:11:35 1999
@@ -18,7 +18,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--;
diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/ll_char_wr.S linux/arch/arm/lib/ll_char_wr.S
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/arch/arm/lib/semaphore.S linux/arch/arm/lib/semaphore.S
--- v2.3.6/linux/arch/arm/lib/semaphore.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/semaphore.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/mm/fault-common.c linux/arch/arm/mm/fault-common.c
--- v2.3.6/linux/arch/arm/mm/fault-common.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/fault-common.c	Thu Jun 17 01:11:35 1999
@@ -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));
 
@@ -77,6 +66,27 @@
 	} while(0);
 
 	printk("\n");
+}
+
+/*
+ * 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 mode, 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, mode);
 
 	do_exit(SIGKILL);
diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/ioremap.c linux/arch/arm/mm/ioremap.c
--- v2.3.6/linux/arch/arm/mm/ioremap.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/ioremap.c	Thu Jun 17 01:11:35 1999
@@ -115,19 +115,19 @@
 {
 	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..
diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-arm2,3.S linux/arch/arm/mm/proc-arm2,3.S
--- v2.3.6/linux/arch/arm/mm/proc-arm2,3.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-arm2,3.S	Thu Jun 17 01:11:35 1999
@@ -202,15 +202,11 @@
 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.
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-arm6,7.S linux/arch/arm/mm/proc-arm6,7.S
--- v2.3.6/linux/arch/arm/mm/proc-arm6,7.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-arm6,7.S	Thu Jun 17 01:11:35 1999
@@ -74,14 +74,14 @@
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
 		ldr	r2, [r1, #TSK_ADDR_LIMIT]
+		ldr	r3, [r1, #TSS_MEMMAP]		@ Page table pointer
 		teq	r2, #0
 		moveq	r2, #DOM_KERNELDOMAIN
 		movne	r2, #DOM_USERDOMAIN
 		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
diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-sa110.S linux/arch/arm/mm/proc-sa110.S
--- v2.3.6/linux/arch/arm/mm/proc-sa110.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-sa110.S	Thu Jun 17 01:11:35 1999
@@ -29,11 +29,11 @@
 		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
 		add	r1, ip, #16384			@ only necessary for 16k
 1:		ldr	r3, [ip], #32
@@ -226,12 +226,12 @@
 		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, #TSK_ADDR_LIMIT]
 		ldr	r4, [r1, #TSS_MEMMAP]		@ Page table pointer
+		teq	r5, #0
+		moveq	r5, #DOM_KERNELDOMAIN
+		movne	r5, #DOM_USERDOMAIN
+		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,
@@ -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 (TLB bypasses WB)
 		mov	pc, lr
 
 /*
@@ -318,6 +319,7 @@
 		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, 4		@ drain WB (TLB bypasses WB)
 		mov	pc, lr
 
 /*
diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/ARM-gcc.h linux/arch/arm/nwfpe/ARM-gcc.h
--- v2.3.6/linux/arch/arm/nwfpe/ARM-gcc.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/ARM-gcc.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/ChangeLog linux/arch/arm/nwfpe/ChangeLog
--- v2.3.6/linux/arch/arm/nwfpe/ChangeLog	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/ChangeLog	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/Makefile linux/arch/arm/nwfpe/Makefile
--- v2.3.6/linux/arch/arm/nwfpe/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/Makefile	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/config.h linux/arch/arm/nwfpe/config.h
--- v2.3.6/linux/arch/arm/nwfpe/config.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/config.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/double_cpdo.c linux/arch/arm/nwfpe/double_cpdo.c
--- v2.3.6/linux/arch/arm/nwfpe/double_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/double_cpdo.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/entry.S linux/arch/arm/nwfpe/entry.S
--- v2.3.6/linux/arch/arm/nwfpe/entry.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/entry.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/entry26.S linux/arch/arm/nwfpe/entry26.S
--- v2.3.6/linux/arch/arm/nwfpe/entry26.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/entry26.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/extended_cpdo.c linux/arch/arm/nwfpe/extended_cpdo.c
--- v2.3.6/linux/arch/arm/nwfpe/extended_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/extended_cpdo.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.c linux/arch/arm/nwfpe/fpa11.c
--- v2.3.6/linux/arch/arm/nwfpe/fpa11.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.h linux/arch/arm/nwfpe/fpa11.h
--- v2.3.6/linux/arch/arm/nwfpe/fpa11.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.inl linux/arch/arm/nwfpe/fpa11.inl
--- v2.3.6/linux/arch/arm/nwfpe/fpa11.inl	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.inl	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdo.c linux/arch/arm/nwfpe/fpa11_cpdo.c
--- v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cpdo.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdt.c linux/arch/arm/nwfpe/fpa11_cpdt.c
--- v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdt.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cpdt.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cprt.c linux/arch/arm/nwfpe/fpa11_cprt.c
--- v2.3.6/linux/arch/arm/nwfpe/fpa11_cprt.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cprt.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.c linux/arch/arm/nwfpe/fpmodule.c
--- v2.3.6/linux/arch/arm/nwfpe/fpmodule.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,167 @@
+/*
+    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)
+{
+#if 0
+  printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags,
+	 __builtin_return_address(0), userRegisters[15]);
+#endif
+
+  float_exception_flags |= flags;
+  if (readFPSR() & (flags << 16))
+  {
+    /* raise exception */
+    C_SYMBOL_NAME(fp_send_sig)(SIGFPE,C_SYMBOL_NAME(current),1);
+  }
+  else
+  {
+    /* set the cumulative exceptions flags */
+    writeFPSR(flags);
+  }
+}
diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.h linux/arch/arm/nwfpe/fpmodule.h
--- v2.3.6/linux/arch/arm/nwfpe/fpmodule.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.inl linux/arch/arm/nwfpe/fpmodule.inl
--- v2.3.6/linux/arch/arm/nwfpe/fpmodule.inl	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.inl	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpopcode.c linux/arch/arm/nwfpe/fpopcode.c
--- v2.3.6/linux/arch/arm/nwfpe/fpopcode.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpopcode.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpopcode.h linux/arch/arm/nwfpe/fpopcode.h
--- v2.3.6/linux/arch/arm/nwfpe/fpopcode.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpopcode.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpsr.h linux/arch/arm/nwfpe/fpsr.h
--- v2.3.6/linux/arch/arm/nwfpe/fpsr.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpsr.h	Thu Jun 17 01:11:35 1999
@@ -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	0x00100000	/* use alternative C-flag definition
+				   for compares */
+#define BIT_EP	0x00080000	/* use expanded packed decimal format */
+#define BIT_SO	0x00040000	/* select synchronous operation of FPA */
+#define BIT_NE	0x00020000	/* NaN exception bit */
+#define BIT_ND	0x00010000	/* 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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/milieu.h linux/arch/arm/nwfpe/milieu.h
--- v2.3.6/linux/arch/arm/nwfpe/milieu.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/milieu.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/single_cpdo.c linux/arch/arm/nwfpe/single_cpdo.c
--- v2.3.6/linux/arch/arm/nwfpe/single_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/single_cpdo.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat-macros linux/arch/arm/nwfpe/softfloat-macros
--- v2.3.6/linux/arch/arm/nwfpe/softfloat-macros	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat-macros	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat-specialize linux/arch/arm/nwfpe/softfloat-specialize
--- v2.3.6/linux/arch/arm/nwfpe/softfloat-specialize	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat-specialize	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat.c linux/arch/arm/nwfpe/softfloat.c
--- v2.3.6/linux/arch/arm/nwfpe/softfloat.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat.h linux/arch/arm/nwfpe/softfloat.h
--- v2.3.6/linux/arch/arm/nwfpe/softfloat.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/arch/arm/vmlinux-armv.lds linux/arch/arm/vmlinux-armv.lds
--- v2.3.6/linux/arch/arm/vmlinux-armv.lds	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/vmlinux-armv.lds	Thu Jun 17 01:11:35 1999
@@ -7,50 +7,64 @@
 ENTRY(_start)
 SECTIONS
 {
-  _text = .;			/* Text and read-only data */
-  .text : {
+  _text = .;			/* Text and read-only data	*/
+  .text : { }			/* Set text start address	*/
+
+  __init_begin = .;		/* Init code and data		*/
+  .text.init : { *(.text.init) }
+  .data.init : { *(.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 : {		/* Real text segment		*/
 	*(.text)
 	*(.fixup)
 	*(.gnu.warning)
-	} = 0x9090
+	}
+
   .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) }
   .kstrtab : { *(.kstrtab) }
 
-  . = ALIGN(16);		/* Exception table */
+  . = ALIGN(16);		/* Exception table		*/
   __start___ex_table = .;
   __ex_table : { *(__ex_table) }
   __stop___ex_table = .;
 
-  __start___ksymtab = .;	/* Kernel symbol table */
+  __start___ksymtab = .;	/* Kernel symbol table		*/
   __ksymtab : { *(__ksymtab) }
   __stop___ksymtab = .;
 
-  _etext = .;			/* End of text section */
+  _etext = .;			/* End of text section		*/
 
   . = ALIGN(8192);
-  .data : {			/* Data */
+  .data : {			/* Data				*/
 	*(.init.task)
 	*(.data)
 	CONSTRUCTORS
 	}
 
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(4096);
-  __init_end = .;
+  _edata = .;			/* End of data section		*/
 
-  __bss_start = .;		/* BSS */
+  __bss_start = .;		/* BSS				*/
   .bss : {
 	*(.bss)
 	}
   _end = . ;
 
-  /* Stabs debugging sections.  */
+				/* Stabs debugging sections.	*/
   .stab 0 : { *(.stab) }
   .stabstr 0 : { *(.stabstr) }
   .stab.excl 0 : { *(.stab.excl) }
diff -u --recursive --new-file v2.3.6/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.6/linux/arch/i386/defconfig	Mon Jun  7 22:12:01 1999
+++ linux/arch/i386/defconfig	Thu Jun 17 01:27:19 1999
@@ -95,10 +95,9 @@
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
 CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
 # CONFIG_BLK_DEV_OFFBOARD is not set
 # CONFIG_BLK_DEV_AEC6210 is not set
-CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
@@ -355,7 +354,6 @@
 #
 # CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-# CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
diff -u --recursive --new-file v2.3.6/linux/arch/i386/kernel/mca.c linux/arch/i386/kernel/mca.c
--- v2.3.6/linux/arch/i386/kernel/mca.c	Mon Jun  7 16:17:59 1999
+++ linux/arch/i386/kernel/mca.c	Sat Jun 19 11:45:28 1999
@@ -148,7 +148,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 #endif
diff -u --recursive --new-file v2.3.6/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.3.6/linux/arch/i386/kernel/smp.c	Wed Jun  2 11:29:13 1999
+++ linux/arch/i386/kernel/smp.c	Wed Jun 16 19:26:27 1999
@@ -1164,6 +1164,7 @@
 }
 
 unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_old_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
 
 /*
@@ -1187,6 +1188,7 @@
 	for (i = 0; i < NR_CPUS; i++) {
 		cpu_number_map[i] = -1;
 		prof_counter[i] = 1;
+		prof_old_multiplier[i] = 1;
 		prof_multiplier[i] = 1;
 	}
 
@@ -1733,6 +1735,10 @@
 	return 0;
 }
 
+static unsigned int calibration_result;
+
+void setup_APIC_timer(unsigned int clocks);
+
 /*
  * Local timer interrupt handler. It does both profiling and
  * process statistics/rescheduling.
@@ -1745,6 +1751,7 @@
 
 void smp_local_timer_interrupt(struct pt_regs * regs)
 {
+	int user = (user_mode(regs) != 0);
 	int cpu = smp_processor_id();
 
 	/*
@@ -1753,25 +1760,34 @@
 	 * updated with atomic operations). This is especially
 	 * useful with a profiling multiplier != 1
 	 */
-	if (!user_mode(regs))
+	if (!user)
 		x86_do_profile(regs->eip);
 
 	if (!--prof_counter[cpu]) {
-		int user=0,system=0;
+		int system = 1 - user;
 		struct task_struct * p = current;
 
 		/*
+		 * The multiplier may have changed since the last time we got
+		 * to this point as a result of the user writing to
+		 * /proc/profile.  In this case we need to adjust the APIC
+		 * timer accordingly.
+		 *
+		 * Interrupts are already masked off at this point.
+		 */
+               prof_counter[cpu] = prof_multiplier[cpu];
+               if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
+                       setup_APIC_timer(calibration_result/prof_counter[cpu]);
+                       prof_old_multiplier[cpu] = prof_counter[cpu];
+               }
+
+		/*
 		 * After doing the above, we need to make like
 		 * a normal interrupt - otherwise timer interrupts
 		 * ignore the global interrupt lock, which is the
 		 * WrongThing (tm) to do.
 		 */
 
-		if (user_mode(regs))
-			user=1;
-		else
-			system=1;
-
  		irq_enter(cpu, 0);
 		update_one_process(p, 1, user, system, cpu);
 		if (p->pid) {
@@ -1791,7 +1807,6 @@
 			kstat.per_cpu_system[cpu] += system;
 
 		}
-		prof_counter[cpu]=prof_multiplier[cpu];
 		irq_exit(cpu, 0);
 	}
 
@@ -2064,8 +2079,6 @@
 	return calibration_result;
 }
 
-static unsigned int calibration_result;
-
 void __init setup_APIC_clock(void)
 {
 	unsigned long flags;
@@ -2117,13 +2130,10 @@
 /*
  * the frequency of the profiling timer can be changed
  * by writing a multiplier value into /proc/profile.
- *
- * usually you want to run this on all CPUs ;)
  */
 int setup_profiling_timer(unsigned int multiplier)
 {
-	int cpu = smp_processor_id();
-	unsigned long flags;
+	int i;
 
 	/*
 	 * Sanity check. [at least 500 APIC cycles should be
@@ -2133,11 +2143,14 @@
 	if ( (!multiplier) || (calibration_result/multiplier < 500))
 		return -EINVAL;
 
-	save_flags(flags);
-	cli();
-	setup_APIC_timer(calibration_result/multiplier);
-	prof_multiplier[cpu]=multiplier;
-	restore_flags(flags);
+	/* 
+	 * Set the new multiplier for each CPU.  CPUs don't start using the
+	 * new values until the next timer interrupt in which they do process
+	 * accounting.  At that time they also adjust their APIC timers
+	 * accordingly.
+	 */
+	for (i = 0; i < NR_CPUS; ++i)
+		prof_multiplier[i] = multiplier;
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.6/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.3.6/linux/arch/i386/mm/init.c	Tue Jun  8 10:49:48 1999
+++ linux/arch/i386/mm/init.c	Wed Jun 16 19:26:27 1999
@@ -159,10 +159,10 @@
 			reserved++;
 		else if (PageSwapCache(mem_map+i))
 			cached++;
-		else if (!atomic_read(&mem_map[i].count))
+		else if (!page_count(mem_map+i))
 			free++;
 		else
-			shared += atomic_read(&mem_map[i].count) - 1;
+			shared += page_count(mem_map+i) - 1;
 	}
 	printk("%d pages of RAM\n",total);
 	printk("%d reserved pages\n",reserved);
@@ -449,7 +449,7 @@
 				reservedpages++;
 			continue;
 		}
-		atomic_set(&mem_map[MAP_NR(tmp)].count, 1);
+		set_page_count(mem_map+MAP_NR(tmp), 1);
 #ifdef CONFIG_BLK_DEV_INITRD
 		if (!initrd_start || (tmp < initrd_start || tmp >=
 		    initrd_end))
@@ -475,7 +475,7 @@
 	addr = (unsigned long)(&__init_begin);
 	for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
 		mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved);
-		atomic_set(&mem_map[MAP_NR(addr)].count, 1);
+		set_page_count(mem_map+MAP_NR(addr), 1);
 		free_page(addr);
 	}
 	printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
@@ -494,9 +494,9 @@
 		if (PageReserved(mem_map+i))
 			continue;
 		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
+		if (!page_count(mem_map+i))
 			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
+		val->sharedram += page_count(mem_map+i) - 1;
 	}
 	val->totalram <<= PAGE_SHIFT;
 	val->sharedram <<= PAGE_SHIFT;
diff -u --recursive --new-file v2.3.6/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.3.6/linux/arch/sparc/defconfig	Mon May 31 22:08:10 1999
+++ linux/arch/sparc/defconfig	Thu Jun 17 01:08:50 1999
@@ -259,7 +259,6 @@
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 CONFIG_SMB_FS=m
-CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
 # CONFIG_NCPFS_PACKET_SIGNING is not set
 # CONFIG_NCPFS_IOCTL_LOCKING is not set
diff -u --recursive --new-file v2.3.6/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.3.6/linux/arch/sparc/kernel/signal.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/kernel/signal.c	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $
+/*  $Id: signal.c,v 1.92 1999/06/14 05:23:53 davem Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -1194,6 +1194,7 @@
 			default:
 				lock_kernel();
 				sigaddset(&current->signal, signr);
+				recalc_sigpending(current);
 				current->flags |= PF_SIGNALED;
 				do_exit(exit_code);
 				/* NOT REACHED */
diff -u --recursive --new-file v2.3.6/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.3.6/linux/arch/sparc/kernel/sys_sunos.c	Wed Jun  9 14:44:25 1999
+++ linux/arch/sparc/kernel/sys_sunos.c	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.98 1999/06/09 08:23:39 davem Exp $
+/* $Id: sys_sunos.c,v 1.99 1999/06/11 11:40:39 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -197,7 +197,7 @@
 	 * fool it, but this should catch most mistakes.
 	 */
 	freepages = buffermem >> PAGE_SHIFT;
-        freepages += page_cache_size;
+	freepages += atomic_read(&page_cache_size);
 	freepages >>= 1;
 	freepages += nr_free_pages;
 	freepages += nr_swap_pages;
@@ -209,7 +209,7 @@
 	 * Ok, we have probably got enough memory - let it rip.
 	 */
 	current->mm->brk = brk;
-	do_brk(oldbrk, newbrk-oldbrk)
+	do_brk(oldbrk, newbrk-oldbrk);
 	retval = 0;
 out:
 	up(&current->mm->mmap_sem);
diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.3.6/linux/arch/sparc64/defconfig	Wed Jun  9 14:44:25 1999
+++ linux/arch/sparc64/defconfig	Thu Jun 17 01:08:50 1999
@@ -299,7 +299,6 @@
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 CONFIG_SMB_FS=m
-CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
 # CONFIG_NCPFS_PACKET_SIGNING is not set
 # CONFIG_NCPFS_IOCTL_LOCKING is not set
diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.3.6/linux/arch/sparc64/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
+++ linux/arch/sparc64/kernel/ptrace.c	Thu Jun 17 01:08:50 1999
@@ -34,7 +34,7 @@
  * and that it is in the task area before calling this: this routine does
  * no checking.
  */
-static pte_t *get_page(struct task_struct * tsk,
+static pte_t *ptrace_get_page(struct task_struct * tsk,
 	struct vm_area_struct * vma, unsigned long addr, int write)
 {
 	pgd_t * pgdir;
@@ -121,7 +121,7 @@
 	pte_t * pgtable;
 	unsigned long page, retval;
 	
-	if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0;
+	if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0;
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	if (MAP_NR(page) >= max_mapnr)
@@ -138,7 +138,7 @@
 	pte_t *pgtable;
 	unsigned long page;
 
-	if (!(pgtable = get_page (tsk, vma, addr, 1))) return;
+	if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return;
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	flush_cache_page(vma, addr);
@@ -166,7 +166,7 @@
 	unsigned long page;
 	unsigned int retval;
 	
-	if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0;
+	if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0;
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	if (MAP_NR(page) >= max_mapnr)
@@ -183,7 +183,7 @@
 	pte_t *pgtable;
 	unsigned long page;
 
-	if (!(pgtable = get_page (tsk, vma, addr, 1))) return;
+	if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return;
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	flush_cache_page(vma, addr);
@@ -941,7 +941,7 @@
 				pt_error_return(regs, EIO);
 				goto flush_and_out;
 			}
-			pgtable = get_page (child, vma, src, 0);
+			pgtable = ptrace_get_page (child, vma, src, 0);
 			up(&child->mm->mmap_sem);
 			if (src & ~PAGE_MASK) {
 				curlen = PAGE_SIZE - (src & ~PAGE_MASK);
@@ -988,7 +988,7 @@
 				pt_error_return(regs, EIO);
 				goto flush_and_out;
 			}
-			pgtable = get_page (child, vma, dest, 1);
+			pgtable = ptrace_get_page (child, vma, dest, 1);
 			up(&child->mm->mmap_sem);
 			if (dest & ~PAGE_MASK) {
 				curlen = PAGE_SIZE - (dest & ~PAGE_MASK);
diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
--- v2.3.6/linux/arch/sparc64/kernel/signal.c	Wed Jun  9 14:44:25 1999
+++ linux/arch/sparc64/kernel/signal.c	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.40 1999/06/02 19:19:52 jj Exp $
+/*  $Id: signal.c,v 1.41 1999/06/14 05:23:58 davem Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -898,6 +898,7 @@
 			default:
 				lock_kernel();
 				sigaddset(&current->signal, signr);
+				recalc_sigpending(current);
 				current->flags |= PF_SIGNALED;
 				do_exit(exit_code);
 				/* NOT REACHED */
diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- v2.3.6/linux/arch/sparc64/kernel/signal32.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/kernel/signal32.c	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.47 1998/10/13 09:07:40 davem Exp $
+/*  $Id: signal32.c,v 1.48 1999/06/14 05:24:01 davem Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -1336,6 +1336,7 @@
 			default:
 				lock_kernel();
 				sigaddset(&current->signal, signr);
+				recalc_sigpending(current);
 				current->flags |= PF_SIGNALED;
 				do_exit(exit_code);
 				/* NOT REACHED */
diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.3.6/linux/arch/sparc64/kernel/sys_sunos32.c	Wed Jun  9 14:44:25 1999
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Thu Jun 17 01:08:50 1999
@@ -164,7 +164,7 @@
 	 * fool it, but this should catch most mistakes.
 	 */
 	freepages = buffermem >> PAGE_SHIFT;
-        freepages += page_cache_size;
+	freepages += atomic_read(&page_cache_size);
 	freepages >>= 1;
 	freepages += nr_free_pages;
 	freepages += nr_swap_pages;
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/Config.in linux/drivers/acorn/block/Config.in
--- v2.3.6/linux/drivers/acorn/block/Config.in	Fri May  8 00:42:38 1998
+++ linux/drivers/acorn/block/Config.in	Thu Jun 17 01:11:35 1999
@@ -4,15 +4,12 @@
 mainmenu_option next_comment
 comment 'Acorn-specific block devices'
 
-bool '   Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS
-if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then
-  dep_tristate '    ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_BLK_DEV_IDE
-  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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/Makefile linux/drivers/acorn/block/Makefile
--- v2.3.6/linux/drivers/acorn/block/Makefile	Mon Feb 16 13:49:36 1998
+++ linux/drivers/acorn/block/Makefile	Thu Jun 17 01:11:35 1999
@@ -14,29 +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
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
-  L_OBJS += ide-ics.o
+ifeq ($(CONFIG_BLK_DEV_FD1772),y)
+  L_OBJS += fd1772.o fd1772dma.o
 else
-  ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m)
-    M_OBJS += ide-ics.o
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
-  L_OBJS += ide-rapide.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m)
-    M_OBJS += ide-rapide.o
+  ifeq ($(CONFIG_BLK_DEV_FD1772),m)
+    M_OBJS += fd1772_mod.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c
--- v2.3.6/linux/drivers/acorn/block/fd1772.c	Sat May 15 15:05:35 1999
+++ linux/drivers/acorn/block/fd1772.c	Thu Jun 17 01:11:35 1999
@@ -114,6 +114,8 @@
  *		  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
  */
 
 #include <linux/sched.h>
@@ -136,14 +138,14 @@
 #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>
 
 #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
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/fd1772dma.S linux/drivers/acorn/block/fd1772dma.S
--- v2.3.6/linux/drivers/acorn/block/fd1772dma.S	Fri May  8 00:42:38 1998
+++ linux/drivers/acorn/block/fd1772dma.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/ide-ics.c linux/drivers/acorn/block/ide-ics.c
--- v2.3.6/linux/drivers/acorn/block/ide-ics.c	Tue Jun  1 23:25:48 1999
+++ linux/drivers/acorn/block/ide-ics.c	Wed Dec 31 16:00:00 1969
@@ -1,295 +0,0 @@
-/*
- * linux/arch/arm/drivers/block/ide-ics.c
- *
- * Copyright (c) 1996,1997 Russell King.
- *
- * Changelog:
- *  08-06-1996	RMK	Created
- *  12-09-1997	RMK	Added interrupt enable/disable
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/hdreg.h>
-
-#include <asm/ecard.h>
-#include <asm/io.h>
-
-#include "../../block/ide.h"
-
-/*
- * Maximum number of interfaces per card
- */
-#define MAX_IFS	2
-
-#define ICS_IDENT_OFFSET		0x8a0
-
-#define ICS_ARCIN_V5_INTRSTAT		0x000
-#define ICS_ARCIN_V5_INTROFFSET		0x001
-#define ICS_ARCIN_V5_IDEOFFSET		0xa00
-#define ICS_ARCIN_V5_IDEALTOFFSET	0xae0
-#define ICS_ARCIN_V5_IDESTEPPING	4
-
-#define ICS_ARCIN_V6_IDEOFFSET_1	0x800
-#define ICS_ARCIN_V6_INTROFFSET_1	0x880
-#define ICS_ARCIN_V6_INTRSTAT_1		0x8a4
-#define ICS_ARCIN_V6_IDEALTOFFSET_1	0x8e0
-#define ICS_ARCIN_V6_IDEOFFSET_2	0xc00
-#define ICS_ARCIN_V6_INTROFFSET_2	0xc80
-#define ICS_ARCIN_V6_INTRSTAT_2		0xca4
-#define ICS_ARCIN_V6_IDEALTOFFSET_2	0xce0
-#define ICS_ARCIN_V6_IDESTEPPING	4
-
-static const card_ids icside_cids[] = {
-	{ MANU_ICS, PROD_ICS_IDE },
-	{ 0xffff, 0xffff }
-};
-
-typedef enum {
-	ics_if_unknown,
-	ics_if_arcin_v5,
-	ics_if_arcin_v6
-} iftype_t;
-
-static struct expansion_card *ec[MAX_ECARDS];
-static int result[MAX_ECARDS][MAX_IFS];
-
-
-/* ---------------- Version 5 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose  : enable interrupts from card
- */
-static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int memc_port = (unsigned int)ec->irq_data;
-	outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose  : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int memc_port = (unsigned int)ec->irq_data;
-	inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v5 = {
-	icside_irqenable_arcin_v5,
-	icside_irqdisable_arcin_v5,
-	NULL,
-	NULL
-};
-
-
-/* ---------------- Version 6 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose  : enable interrupts from card
- */
-static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int ide_base_port = (unsigned int)ec->irq_data;
-	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
-	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose  : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int ide_base_port = (unsigned int)ec->irq_data;
-	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
-	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v6 = {
-	icside_irqenable_arcin_v6,
-	icside_irqdisable_arcin_v6,
-	NULL,
-	NULL
-};
-
-
-
-/* Prototype: icside_identifyif (struct expansion_card *ec)
- * Purpose  : identify IDE interface type
- * Notes    : checks the description string
- */
-static iftype_t icside_identifyif (struct expansion_card *ec)
-{
-	unsigned int addr;
-	iftype_t iftype;
-	int id = 0;
-
-	iftype = ics_if_unknown;
-
-	addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
-
-	id = inb (addr) & 1;
-	id |= (inb (addr + 1) & 1) << 1;
-	id |= (inb (addr + 2) & 1) << 2;
-	id |= (inb (addr + 3) & 1) << 3;
-
-	switch (id) {
-	case 0: /* A3IN */
-		printk ("icside: A3IN unsupported\n");
-		break;
-
-	case 1: /* A3USER */
-		printk ("icside: A3USER unsupported\n");
-		break;
-
-	case 3:	/* ARCIN V6 */
-		printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
-		iftype = ics_if_arcin_v6;
-		break;
-
-	case 15:/* ARCIN V5 (no id) */
-		printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
-		iftype = ics_if_arcin_v5;
-		break;
-
-	default:/* we don't know - complain very loudly */
-		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: defaulting to ARCIN V5\n");
-		iftype = ics_if_arcin_v5;
-		break;
-	}
-
-	return iftype;
-}
-
-static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq)
-{
-	hw_regs_t hw;
-	int i;
-
-	memset(&hw, 0, sizeof(hw));
-
-	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
-		hw.io_ports[i] = (ide_ioreg_t)dataport;
-		dataport += 1 << stepping;
-	}
-	hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport;
-	hw.irq = irq;
-
-	return ide_register_hw(&hw, NULL);
-}
-
-/* Prototype: icside_register (struct expansion_card *ec)
- * Purpose  : register an ICS IDE card with the IDE driver
- * Notes    : we make sure that interrupts are disabled from the card
- */
-static inline void icside_register (struct expansion_card *ec, int index)
-{
-	unsigned long port;
-
-	result[index][0] = -1;
-	result[index][1] = -1;
-
-	switch (icside_identifyif (ec)) {
-	case ics_if_unknown:
-	default:
-		printk ("** Warning: ICS IDE Interface unrecognised! **\n");
-		break;
-
-	case ics_if_arcin_v5:
-		port = ecard_address (ec, ECARD_MEMC, 0);
-		ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
-		ec->irqmask = 1;
-		ec->irq_data = (void *)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,
-							ICS_ARCIN_V5_IDESTEPPING,
-							ec->irq);
-		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;
-		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);
-
-		result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
-							port + ICS_ARCIN_V6_IDEALTOFFSET_1,
-							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
-		result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
-							port + ICS_ARCIN_V6_IDEALTOFFSET_2,
-							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
-		break;
-	}		
-}
-
-int icside_init (void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		ec[i] = NULL;
-
-	ecard_startfind ();
-
-	for (i = 0; ; i++) {
-		if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
-			break;
-
-		ecard_claim (ec[i]);
-		icside_register (ec[i], i);
-	}
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
-			ecard_release (ec[i]);
-			ec[i] = NULL;
-		}
-	return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
-	return icside_init();
-}
-
-void cleanup_module (void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i]) {
-			if (result[i][0] >= 0)
-				ide_unregister (result[i][0]);
-
-			if (result[i][1] >= 0)
-				ide_unregister (result[i][1]);
-				
-			ecard_release (ec[i]);
-			ec[i] = NULL;
-		}
-}
-#endif
-
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/ide-rapide.c linux/drivers/acorn/block/ide-rapide.c
--- v2.3.6/linux/drivers/acorn/block/ide-rapide.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/block/ide-rapide.c	Wed Dec 31 16:00:00 1969
@@ -1,87 +0,0 @@
-/*
- * linux/arch/arm/drivers/block/ide-rapide.c
- *
- * Copyright (c) 1996-1998 Russell King.
- *
- * Changelog:
- *  08-06-1996	RMK	Created
- *  13-04-1998	RMK	Added manufacturer and product IDs
- */
-
-#include <linux/module.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <asm/ecard.h>
-#include <asm/ide.h>
-
-#include "../../block/ide.h"
-
-static const card_ids rapide_cids[] = {
-	{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
-	{ 0xffff, 0xffff }
-};
-
-static struct expansion_card *ec[MAX_ECARDS];
-static int result[MAX_ECARDS];
-
-static inline int rapide_register(struct expansion_card *ec)
-{
-	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
-	ide_ioregspec_t spec;
-
-	spec.base = port;
-	spec.ctrl = port + 0x206;
-	spec.offset = 1 << 4;
-	spec.irq = ec->irq;
-
-	return ide_register_port(&spec);
-}
-
-int rapide_init(void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		ec[i] = NULL;
-
-	ecard_startfind();
-
-	for (i = 0; ; i++) {
-		if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
-			break;
-
-		ecard_claim(ec[i]);
-		result[i] = rapide_register(ec[i]);
-	}
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i] && result[i] < 0) {
-			ecard_release(ec[i]);
-			ec[i] = NULL;
-	}
-	return 0;
-}
-
-#ifdef MODULE
-
-int init_module (void)
-{
-	return rapide_init();
-}
-
-void cleanup_module (void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i]) {
-			unsigned long port;
-			port = ecard_address(ec[i], ECARD_MEMC, 0);
-
-			ide_unregister_port(port, ec[i]->irq, 16);
-			ecard_release(ec[i]);
-			ec[i] = NULL;
-		}
-}
-#endif
-
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/mfm.S linux/drivers/acorn/block/mfm.S
--- v2.3.6/linux/drivers/acorn/block/mfm.S	Wed May 20 18:54:37 1998
+++ linux/drivers/acorn/block/mfm.S	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/mfmhd.c linux/drivers/acorn/block/mfmhd.c
--- v2.3.6/linux/drivers/acorn/block/mfmhd.c	Sat May 15 15:05:35 1999
+++ linux/drivers/acorn/block/mfmhd.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/Config.in linux/drivers/acorn/char/Config.in
--- v2.3.6/linux/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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/Makefile linux/drivers/acorn/char/Makefile
--- v2.3.6/linux/drivers/acorn/char/Makefile	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/char/Makefile	Thu Jun 17 01:11:35 1999
@@ -9,20 +9,21 @@
 # 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)
-else
-  ifeq ($(CONFIG_MOUSE),m)
-    MX_OBJS += $(MOUSE_OBJS)
+ifeq ($(MACHINE),rpc)
+  ifeq ($(CONFIG_MOUSE),y)
+    LX_OBJS += mouse_rpc.o
+  else
+    ifeq ($(CONFIG_MOUSE),m)
+      MX_OBJS += mouse_rpc.o
+    endif
   endif
 endif
 
@@ -41,5 +42,7 @@
     M_OBJS += serial-dualsp.o
   endif
 endif
+
+L_OBJS += $(L_OBJS_$(MACHINE))
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/keyb_arc.c linux/drivers/acorn/char/keyb_arc.c
--- v2.3.6/linux/drivers/acorn/char/keyb_arc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/char/keyb_arc.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/keyb_ps2.c linux/drivers/acorn/char/keyb_ps2.c
--- v2.3.6/linux/drivers/acorn/char/keyb_ps2.c	Mon Apr 26 13:31:49 1999
+++ linux/drivers/acorn/char/keyb_ps2.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/mouse_rpc.c linux/drivers/acorn/char/mouse_rpc.c
--- v2.3.6/linux/drivers/acorn/char/mouse_rpc.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/char/mouse_rpc.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/serial-card.c linux/drivers/acorn/char/serial-card.c
--- v2.3.6/linux/drivers/acorn/char/serial-card.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/char/serial-card.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether1.c linux/drivers/acorn/net/ether1.c
--- v2.3.6/linux/drivers/acorn/net/ether1.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/net/ether1.c	Thu Jun 17 01:11:35 1999
@@ -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;
 }	
     
@@ -759,18 +753,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 +1072,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 +1107,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 +1155,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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether3.c linux/drivers/acorn/net/ether3.c
--- v2.3.6/linux/drivers/acorn/net/ether3.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/net/ether3.c	Thu Jun 17 01:11:35 1999
@@ -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,6 +97,8 @@
 
 /*
  * 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); }
@@ -138,7 +138,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 +153,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 +249,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 +335,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 +470,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 +503,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 +595,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 +609,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 +620,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 +653,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 +683,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 +692,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 +862,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 +870,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 +891,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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether3.h linux/drivers/acorn/net/ether3.h
--- v2.3.6/linux/drivers/acorn/net/ether3.h	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/net/ether3.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/etherh.c linux/drivers/acorn/net/etherh.c
--- v2.3.6/linux/drivers/acorn/net/etherh.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/net/etherh.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/Config.in linux/drivers/acorn/scsi/Config.in
--- v2.3.6/linux/drivers/acorn/scsi/Config.in	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/Config.in	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/Makefile linux/drivers/acorn/scsi/Makefile
--- v2.3.6/linux/drivers/acorn/scsi/Makefile	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/Makefile	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/acornscsi.c linux/drivers/acorn/scsi/acornscsi.c
--- v2.3.6/linux/drivers/acorn/scsi/acornscsi.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/scsi/acornscsi.c	Thu Jun 17 01:11:35 1999
@@ -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 %X 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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/acornscsi.h linux/drivers/acorn/scsi/acornscsi.h
--- v2.3.6/linux/drivers/acorn/scsi/acornscsi.h	Sun Dec 28 09:06:25 1997
+++ linux/drivers/acorn/scsi/acornscsi.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/arxescsi.c linux/drivers/acorn/scsi/arxescsi.c
--- v2.3.6/linux/drivers/acorn/scsi/arxescsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/arxescsi.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/arxescsi.h linux/drivers/acorn/scsi/arxescsi.h
--- v2.3.6/linux/drivers/acorn/scsi/arxescsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/arxescsi.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/cumana_2.c linux/drivers/acorn/scsi/cumana_2.c
--- v2.3.6/linux/drivers/acorn/scsi/cumana_2.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/cumana_2.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/eesox.c linux/drivers/acorn/scsi/eesox.c
--- v2.3.6/linux/drivers/acorn/scsi/eesox.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/eesox.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c
--- v2.3.6/linux/drivers/acorn/scsi/fas216.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/scsi/fas216.c	Thu Jun 17 01:11:35 1999
@@ -24,9 +24,9 @@
  *  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
  *
  * Todo:
- *  - tighten up the MESSAGE_REJECT support.
  *  - allow individual devices to enable sync xfers.
  */
 
@@ -57,7 +57,7 @@
 
 #define VER_MAJOR	0
 #define VER_MINOR	0
-#define VER_PATCH	4
+#define VER_PATCH	5
 
 #define SCSI2_TAG
 
@@ -86,6 +86,8 @@
  */
 #define SCSI2_SYNC
 
+#define SCSI2_WIDE
+
 #undef DEBUG_CONNECT
 #undef DEBUG_BUSSERVICE
 #undef DEBUG_FUNCTIONDONE
@@ -132,8 +134,8 @@
 	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("      msgs 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",
@@ -144,10 +146,10 @@
 		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",
+		printk("    busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
 			i, info->busyluns[i], 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",
 		info->dma.transfer_type, info->dma.setup,
@@ -192,19 +194,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 +264,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 +336,240 @@
 		outb(info->scsi.cfg[2], REG_CNTL3(info));
 }
 
+/* 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 void
+fas216_handlesync(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { sync, async, none, reject } res = none;
+
+#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
+
+	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:
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+	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:
+		break;
+	}
+}
+
+/* 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 void
+fas216_handlewide(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { wide, bit8, none, reject } res = none;
+
+#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
+
+	switch (res) {
+	case wide:
+		dev->wide_xfer = msg[3];
+		break;
+
+	case reject:
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+	case bit8:
+		dev->wide_xfer = 0;
+		break;
+
+	case none:
+		break;
+	}
+}
+
 /* 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 +605,9 @@
 	residual -= bytes_transferred;
 	ptr += bytes_transferred;
 
+	if (residual == 0)
+		ptr = NULL;
+
 	info->scsi.SCp.ptr = ptr;
 	info->scsi.SCp.this_residual = residual;
 }
@@ -353,7 +623,7 @@
 {
 	unsigned int residual;
 	char *ptr;
-	int correction;
+	int correction = 0;
 
 	fas216_checkmagic(info, "fas216_pio");
 
@@ -361,23 +631,24 @@
 	ptr = info->scsi.SCp.ptr;
 
 	if (direction == DMA_OUT) {
-	    	while (residual > 0) {
-			if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
+//	    	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;
+//			}
+//			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) {
+//	    	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 (inb(REG_STAT(info)) & STAT_INT)
+//				break;
+//		}
 	}
 
 	ptr -= correction;
@@ -549,10 +820,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 +836,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 +864,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 +882,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 +910,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 +947,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 +1011,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,6 +1035,28 @@
 #endif
 }
 
+static unsigned char fas216_get_msg_byte(FAS216_Info *info)
+{
+	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));
+
+	return inb(REG_FF(info));
+}
+
 /* Function: void fas216_message(FAS216_Info *info)
  * Purpose : handle a function done interrupt from FAS216 chip
  * Params  : info - interface which caused function done interrupt
@@ -765,34 +1071,10 @@
 	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));
-
-		message[1] = inb(REG_FF(info));
-
-		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));
+		message[1] = fas216_get_msg_byte(info);
 
-			message[msglen] = inb(REG_FF(info));
-		}
+		for (msglen = 2; msglen < message[1] + 2; msglen++)
+			message[msglen] = fas216_get_msg_byte(info);
 	}
 
 #ifdef DEBUG_MESSAGES
@@ -806,6 +1088,7 @@
 		printk("\n");
 	}
 #endif
+
 	if (info->scsi.phase == PHASE_RECONNECTED) {
 		if (message[0] == SIMPLE_QUEUE_TAG)
 			info->scsi.reconnected.tag = message[1];
@@ -815,14 +1098,22 @@
 
 	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));
 		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),
@@ -843,13 +1134,27 @@
 		break;
 
 	case DISCONNECT:
-		info->scsi.phase = PHASE_DISCONNECT;
+		info->scsi.phase = PHASE_MSGIN_DISCONNECT;
 		break;
 
 	case MESSAGE_REJECT:
-		printk("scsi%d.%c: reject, last message %04X\n",
-			info->host->host_no, fas216_target(info),
-			info->scsi.last_message);
+		switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) {
+		case EXTENDED_MESSAGE | EXTENDED_SDTR << 8:
+			fas216_handlesync(info, message);
+			break;
+
+		case EXTENDED_MESSAGE | EXTENDED_WDTR << 8:
+			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:
 		break;
 
 	case SIMPLE_QUEUE_TAG:
@@ -862,49 +1167,18 @@
 	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;
-				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;
-			}
+			fas216_handlesync(info, message);
 			break;
 
 		case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
-			/* We don't do wide transfers - reject message */
+			fas216_handlewide(info, message);
+			break;
+
 		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;
+			goto reject_message;
 		}
 		break;
 
@@ -912,13 +1186,17 @@
 		printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
 			info->host->host_no, fas216_target(info),
 			message[0]);
-		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;
+		goto reject_message;
 	}
 	outb(CMD_MSGACCEPTED, REG_CMD(info));
+	return;
+
+reject_message:
+	outb(CMD_SETATN, REG_CMD(info));
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
+	msgqueue_flush(&info->scsi.msgs);
+	msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+	info->scsi.phase = PHASE_MSGOUT_EXPECT;
 }
 
 /* Function: void fas216_send_command(FAS216_Info *info)
@@ -935,201 +1213,46 @@
 	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));
-}
-
-/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in selection phase
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- */
-static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
-{
-	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;
-
-	case STAT_STATUS:		/* status phase			*/
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
-
-	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;
-
-		/* load message bytes, but don't forget to miss the first
-		 * byte!
-		 */
-		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-			int i;
-
-			for (i = start; i < msglen; i++)
-				outb(msg[i], REG_FF(info));
-			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;
-
-	case STAT_COMMAND:		/* command phase		*/
-		fas216_send_command(info);
-		return 1;
-
-	default:
-		return 0;
-	}
-}
-
-/* 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!
- */
-static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_dataphase");
-
-	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;
-
-	case STAT_DATAOUT:		/* continue data out phase	*/
-		if (info->scsi.phase == PHASE_DATAOUT) {
-			fas216_starttransfer(info, DMA_OUT, 0);
-			return 1;
-		} else
-			return 0;
-
-	case STAT_STATUS:		/* status in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
 
-	case STAT_MESGIN:		/* message in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
-
-	default:
-		return 0;
-	}
+	info->scsi.phase = PHASE_COMMAND;
 }
 
-/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in after a reconnection
+/* 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_reconnected(FAS216_Info *info, unsigned int stat)
+static void fas216_send_messageout(FAS216_Info *info, int start)
 {
-	fas216_checkmagic(info, "fas216_busservice_reconnected");
-
-	switch (stat & STAT_BUSMASK) {
-	case STAT_MESGIN:
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
-
-	case STAT_STATUS:
-		fas216_finish_reconnect(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs);
 
-	case STAT_DATAOUT:		/* data out phase		*/
-		fas216_finish_reconnect(info);
-		fas216_starttransfer(info, DMA_OUT, 1);
-		return 1;
+	fas216_checkmagic(info, "fas216_send_messageout");
 
-	case STAT_DATAIN:		/* data in phase		*/
-		fas216_finish_reconnect(info);
-		fas216_starttransfer(info, DMA_IN, 0);
-		return 1;
+	outb(CMD_FLUSHFIFO, REG_CMD(info));
 
-	default:
-		return 0;
-	}
-}
+	if (tot_msglen) {
+		struct message *msg;
+		int msgnr = 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);
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+			int i;
 
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
+			for (i = start; i < msg->length; i++)
+				outb(msg->msg[i], REG_FF(info));
 
-		if (msglen == 0)
-			outb(NOP, REG_FF(info));
-		else {
-			char *msg;
+			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
+			start = 0;
+		}
+	} else
+		outb(NOP, REG_FF(info));
 
-			while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-				int i;
+	outb(CMD_TRANSFERINFO, REG_CMD(info));
 
-				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;
-	}
+	info->scsi.phase = PHASE_MSGOUT;
 }
 
 /* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
@@ -1151,91 +1274,150 @@
 	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;
+#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);
+		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 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 STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out     -> Data Out     */
+			fas216_starttransfer(info, DMA_OUT, 0);
+			return;
 
-		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;
+							/* 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;
+
+							/* Reselmsgin   -> Status       */
+		case STATE(STAT_STATUS, PHASE_RECONNECTED):
+			fas216_finish_reconnect(info);
+			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_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_TRANSFERINFO, REG_CMD(info));
+			info->scsi.phase = PHASE_MSGIN;
+			return;
 
-		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;
+							/* 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;
 
-			case STAT_COMMAND:	/* command phase			*/
-				fas216_send_command(info);
-				info->scsi.phase = PHASE_SELECTION;
-				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;
+
+		/* 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;
 
-			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;
+							/* 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));
 
-		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;
+			fas216_send_messageout(info, 0);
+			return;
+		}
 
-		case PHASE_DISCONNECT:
-			printk("scsi%d.%c: disconnect message received, but bus service %s?\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),
 				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;
+			info->scsi.phase = PHASE_MSGOUT_EXPECT;
 			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));
+			return;
 		}
-		break;
+		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;
 
 	default:
 		printk("scsi%d.%c: bus service at step %d?\n",
 			info->host->host_no, fas216_target(info),
 			ssr & IS_BITS);
+		print_debug_list();
 	}
 }
 
@@ -1269,6 +1451,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 +1483,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 +1511,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 +1564,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 +1594,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 +1604,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 +1644,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 +1675,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 +1723,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 +1781,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 +1821,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 +1866,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);
 
@@ -1752,6 +1959,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 +2065,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 +2120,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 +2134,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;
 	}
 }
 
@@ -1908,9 +2220,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));
 
@@ -1963,8 +2277,6 @@
 		SCpnt->scsi_done(SCpnt);
 	}
 
-	printk("\n");
-
 	return result | SCSI_RESET_SUCCESS;
 }
 
@@ -2083,6 +2395,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 +2449,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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/fas216.h linux/drivers/acorn/scsi/fas216.h
--- v2.3.6/linux/drivers/acorn/scsi/fas216.h	Sat Jul 18 11:55:24 1998
+++ linux/drivers/acorn/scsi/fas216.h	Thu Jun 17 01:11:35 1999
@@ -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,7 @@
 		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	disconnectable:1;	/* this command can be disconnected	*/
 		unsigned char	aborting:1;		/* aborting command			*/
@@ -255,6 +260,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 +273,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 +347,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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/msgqueue.c linux/drivers/acorn/scsi/msgqueue.c
--- v2.3.6/linux/drivers/acorn/scsi/msgqueue.c	Sat Jul 18 11:55:24 1998
+++ linux/drivers/acorn/scsi/msgqueue.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/msgqueue.h linux/drivers/acorn/scsi/msgqueue.h
--- v2.3.6/linux/drivers/acorn/scsi/msgqueue.h	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/msgqueue.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/powertec.c linux/drivers/acorn/scsi/powertec.c
--- v2.3.6/linux/drivers/acorn/scsi/powertec.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/powertec.c	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/queue.c linux/drivers/acorn/scsi/queue.c
--- v2.3.6/linux/drivers/acorn/scsi/queue.c	Sat Jul 18 11:55:25 1998
+++ linux/drivers/acorn/scsi/queue.c	Thu Jun 17 01:11:35 1999
@@ -55,6 +55,7 @@
 			q->magic = QUEUE_MAGIC_FREE;
 			q->SCpnt = NULL;
 		}
+		q -= 1;
 		q->next = NULL;
 	}
 
diff -u --recursive --new-file v2.3.6/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.3.6/linux/drivers/block/Config.in	Thu May 13 11:00:08 1999
+++ linux/drivers/block/Config.in	Thu Jun 17 01:11:35 1999
@@ -38,16 +38,16 @@
       bool '   RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
       bool '   Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
       if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
-        bool '     Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
+        bool '     Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+        if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
+          bool '     Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
+        fi
         bool '     Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
         bool '     AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
-        if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
-          bool '     Use DMA by default when available' CONFIG_IDEDMA_AUTO
-        fi
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
           bool '     OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
           bool '     Intel PIIXn chipsets support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX
-          if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
+          if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
             bool '     Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
             bool '     NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
             bool '     VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
@@ -66,7 +66,7 @@
           fi
         fi
       fi
-      if [ "$CONFIG_PPC" = "y" ]; then
+      if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
           bool '   Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
       fi
     fi
@@ -75,11 +75,25 @@
         if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then
           bool '     PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC
           if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then
-            define_bool CONFIG_BLK_DEV_IDEDMA y
             bool '     Use DMA by default' CONFIG_PMAC_IDEDMA_AUTO
           fi
        fi
     fi
+    if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+        bool '   ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
+        if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then
+          bool '     ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS
+          if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+            bool '     Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO
+          fi
+        fi
+        bool '   RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
+    fi
+    if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+         "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+         "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+      define_bool CONFIG_BLK_DEV_IDEDMA y
+    fi
     bool '   Other IDE chipset support' CONFIG_IDE_CHIPSETS
     if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
       comment 'Note: most of these also require special kernel boot parameters'
@@ -170,7 +184,8 @@
      "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
      "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
      "$CONFIG_BLK_DEV_HPT343" = "y" -o \
-     "$CONFIG_BLK_DEV_PIIX" = "y" ]; then
+     "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+     "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
   define_bool CONFIG_BLK_DEV_IDE_MODES y
 else
   define_bool CONFIG_BLK_DEV_IDE_MODES n
diff -u --recursive --new-file v2.3.6/linux/drivers/block/icside.c linux/drivers/block/icside.c
--- v2.3.6/linux/drivers/block/icside.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/icside.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,634 @@
+/*
+ * linux/drivers/block/icside.c
+ *
+ * Copyright (c) 1996,1997 Russell King.
+ *
+ * Changelog:
+ *  08-Jun-1996	RMK	Created
+ *  12-Sep-1997	RMK	Added interrupt enable/disable
+ *  17-Apr-1999	RMK	Added support for V6 EASI
+ *  22-May-1999	RMK	Added support for V6 DMA
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+/*
+ * Maximum number of interfaces per card
+ */
+#define MAX_IFS	2
+
+#define ICS_IDENT_OFFSET		0x8a0
+
+#define ICS_ARCIN_V5_INTRSTAT		0x000
+#define ICS_ARCIN_V5_INTROFFSET		0x001
+#define ICS_ARCIN_V5_IDEOFFSET		0xa00
+#define ICS_ARCIN_V5_IDEALTOFFSET	0xae0
+#define ICS_ARCIN_V5_IDESTEPPING	4
+
+#define ICS_ARCIN_V6_IDEOFFSET_1	0x800
+#define ICS_ARCIN_V6_INTROFFSET_1	0x880
+#define ICS_ARCIN_V6_INTRSTAT_1		0x8a4
+#define ICS_ARCIN_V6_IDEALTOFFSET_1	0x8e0
+#define ICS_ARCIN_V6_IDEOFFSET_2	0xc00
+#define ICS_ARCIN_V6_INTROFFSET_2	0xc80
+#define ICS_ARCIN_V6_INTRSTAT_2		0xca4
+#define ICS_ARCIN_V6_IDEALTOFFSET_2	0xce0
+#define ICS_ARCIN_V6_IDESTEPPING	4
+
+struct cardinfo {
+	unsigned int dataoffset;
+	unsigned int ctrloffset;
+	unsigned int stepping;
+};
+
+static struct cardinfo icside_cardinfo_v5 = {
+	ICS_ARCIN_V5_IDEOFFSET,
+	ICS_ARCIN_V5_IDEALTOFFSET,
+	ICS_ARCIN_V5_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_1 = {
+	ICS_ARCIN_V6_IDEOFFSET_1,
+	ICS_ARCIN_V6_IDEALTOFFSET_1,
+	ICS_ARCIN_V6_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_2 = {
+	ICS_ARCIN_V6_IDEOFFSET_2,
+	ICS_ARCIN_V6_IDEALTOFFSET_2,
+	ICS_ARCIN_V6_IDESTEPPING
+};
+
+static const card_ids icside_cids[] = {
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
+	{ 0xffff, 0xffff }
+};
+
+typedef enum {
+	ics_if_unknown,
+	ics_if_arcin_v5,
+	ics_if_arcin_v6
+} iftype_t;
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v5 = {
+	icside_irqenable_arcin_v5,
+	icside_irqdisable_arcin_v5,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	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
+};
+
+/* Prototype: icside_identifyif (struct expansion_card *ec)
+ * Purpose  : identify IDE interface type
+ * Notes    : checks the description string
+ */
+static iftype_t icside_identifyif (struct expansion_card *ec)
+{
+	unsigned int addr;
+	iftype_t iftype;
+	int id = 0;
+
+	iftype = ics_if_unknown;
+
+	addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+
+	id = inb (addr) & 1;
+	id |= (inb (addr + 1) & 1) << 1;
+	id |= (inb (addr + 2) & 1) << 2;
+	id |= (inb (addr + 3) & 1) << 3;
+
+	switch (id) {
+	case 0: /* A3IN */
+		printk("icside: A3IN unsupported\n");
+		break;
+
+	case 1: /* A3USER */
+		printk("icside: A3USER unsupported\n");
+		break;
+
+	case 3:	/* ARCIN V6 */
+		printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v6;
+		break;
+
+	case 15:/* ARCIN V5 (no id) */
+		printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v5;
+		break;
+
+	default:/* we don't know - complain very loudly */
+		printk("icside: ***********************************\n");
+		printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+		printk("icside: ***********************************\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;
+	}
+
+	return iftype;
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD
+ * DMA controllers.  There is only one DMA controller
+ * per card, which means that only one drive can be
+ * accessed at one time.  NOTE! We do not inforce that
+ * here, but we rely on the main IDE driver spotting
+ * that both interfaces use the same IRQ, which should
+ * guarantee this.
+ *
+ * We are limited by the drives IOR/IOW pulse time.
+ * The closest that we can get to the requirements is
+ * a type C cycle for both mode 1 and mode 2.  However,
+ * this does give a burst of 8MB/s.
+ *
+ * This has been tested with a couple of Conner
+ * Peripherals 1080MB CFS1081A drives, one on each
+ * interface, which deliver about 2MB/s each.  I
+ * believe that this is limited by the lack of
+ * on-board drive cache.
+ */
+#define TABLE_SIZE 2048
+
+static int
+icside_build_dmatable(ide_drive_t *drive, int reading)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct buffer_head *bh = rq->bh;
+	unsigned long addr, size;
+	unsigned char *virt_addr;
+	unsigned int count = 0;
+	dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable;
+
+	do {
+		if (bh == NULL) {
+			/* paging requests have (rq->bh == NULL) */
+			virt_addr = rq->buffer;
+			addr = virt_to_bus (virt_addr);
+			size = rq->nr_sectors << 9;
+		} else {
+			/* group sequential buffers into one large buffer */
+			virt_addr = bh->b_data;
+			addr = virt_to_bus (virt_addr);
+			size = bh->b_size;
+			while ((bh = bh->b_reqnext) != NULL) {
+				if ((addr + size) != virt_to_bus (bh->b_data))
+					break;
+				size += bh->b_size;
+			}
+		}
+
+		if (addr & 3) {
+			printk("%s: misaligned DMA buffer\n", drive->name);
+			return 0;
+		}
+
+		if (size) {
+			if (reading)
+				dma_cache_inv((unsigned int)virt_addr, size);
+			else
+				dma_cache_wback((unsigned int)virt_addr, size);
+		}
+
+		sg[count].address = addr;
+		sg[count].length = size;
+		if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) {
+			printk("%s: DMA table too small\n", drive->name);
+			return 0;
+		}
+	} while (bh != NULL);
+
+	if (!count)
+		printk("%s: empty DMA table?\n", drive->name);
+
+	return count;
+}
+
+static int
+icside_config_drive(ide_drive_t *drive, int mode)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int speed, err;
+
+	if (mode == 2) {
+		speed = XFER_MW_DMA_2;
+		drive->drive_data = 250;
+	} else {
+		speed = XFER_MW_DMA_1;
+		drive->drive_data = 250;
+	}
+
+	/*
+	 * Don't use ide_wait_cmd here - it will
+	 * attempt to set_geometry and recalibrate,
+	 * but for some reason these don't work at
+	 * this point (lost interrupt).
+	 */
+	SELECT_DRIVE(hwif, drive);
+	OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+	OUT_BYTE(speed, IDE_NSECTOR_REG);
+	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+
+	err = ide_wait_stat(drive, DRIVE_READY,
+			    BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD);
+
+	if (err == 0) {
+		drive->id->dma_mword &= 0x00ff;
+		drive->id->dma_mword |= 256 << mode;
+	} else
+		drive->drive_data = 0;
+
+	return err;
+}
+
+static int
+icside_dma_check(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+	int autodma = hwif->autodma;
+
+	if (id && (id->capability & 1) && autodma) {
+		int dma_mode = 0;
+
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive))
+			return hwif->dmaproc(ide_dma_off, drive);
+
+		/* Enable DMA on any drive that has
+		 * UltraDMA (mode 0/1/2) enabled
+		 */
+		if (id->field_valid & 4 && id->dma_ultra & 7)
+			dma_mode = 2;
+		
+		/* Enable DMA on any drive that has mode1
+		 * or mode2 multiword DMA enabled
+		 */
+		if (id->field_valid & 2 && id->dma_mword & 6)
+			dma_mode = id->dma_mword & 4 ? 2 : 1;
+
+		/* Consult the list of known "good" drives */
+		if (ide_dmaproc(ide_dma_good_drive, drive))
+			dma_mode = 1;
+
+		if (dma_mode &&	icside_config_drive(drive, dma_mode) == 0)
+			return hwif->dmaproc(ide_dma_on, drive);
+	}
+	return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+static int
+icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int count, reading = 0;
+
+	switch (func) {
+	case ide_dma_check:
+		return icside_dma_check(drive);
+
+	case ide_dma_read:
+		reading = 1;
+	case ide_dma_write:
+		count = icside_build_dmatable(drive, reading);
+		if (!count)
+			return 1;
+		disable_dma(hwif->hw.dma);
+
+		/* Route the DMA signals to
+		 * to the correct interface.
+		 */
+		outb(hwif->select_data, hwif->config_data);
+
+		/* Select the correct timing
+		 * for this drive
+		 */
+		set_dma_speed(hwif->hw.dma, drive->drive_data);
+
+		set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable, count);
+		set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ
+			     : DMA_MODE_WRITE);
+
+		drive->waiting_for_dma = 1;
+		if (drive->media != ide_disk)
+			return 0;
+
+		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);
+		OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA,
+			 IDE_COMMAND_REG);
+
+	case ide_dma_begin:
+		enable_dma(hwif->hw.dma);
+		return 0;
+
+	case ide_dma_end:
+		drive->waiting_for_dma = 0;
+		disable_dma(hwif->hw.dma);
+		return get_dma_residue(hwif->hw.dma) != 0;
+
+	case ide_dma_test_irq:
+		return inb((unsigned long)hwif->hw.priv) & 1;
+
+	default:
+		return ide_dmaproc(func, drive);
+	}
+}
+
+static unsigned long
+icside_alloc_dmatable(void)
+{
+	static unsigned long dmatable;
+	static unsigned int leftover;
+	unsigned long table;
+
+	if (leftover < TABLE_SIZE) {
+#if PAGE_SIZE == TABLE_SIZE * 2
+		dmatable = __get_free_pages(GFP_KERNEL, 1);
+		leftover = PAGE_SIZE;
+#else
+		dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL);
+		leftover = TABLE_SIZE;
+#endif
+	}
+
+	table = dmatable;
+	if (table) {
+		dmatable += TABLE_SIZE;
+		leftover -= TABLE_SIZE;
+	}
+
+	return table;
+}
+
+static int
+icside_setup_dma(ide_hwif_t *hwif, int autodma)
+{
+	unsigned long table = icside_alloc_dmatable();
+
+	printk("    %s: SG-DMA", hwif->name);
+
+	if (!table)
+		printk(" -- ERROR, unable to allocate DMA table\n");
+	else {
+		hwif->dmatable = (void *)table;
+		hwif->dmaproc = icside_dmaproc;
+		hwif->autodma = autodma;
+
+		printk(" capable%s\n", autodma ?
+			", auto-enable" : "");
+	}
+
+	return hwif->dmatable != NULL;
+}
+#endif
+
+static ide_hwif_t *
+icside_find_hwif(unsigned long dataport)
+{
+	ide_hwif_t *hwif;
+	int index;
+
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport)
+			goto found;
+	}
+
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		if (!hwif->hw.io_ports[IDE_DATA_OFFSET])
+			goto found;
+	}
+
+	return NULL;
+found:
+	return hwif;
+}
+
+static ide_hwif_t *
+icside_setup(unsigned long base, struct cardinfo *info, int irq)
+{
+	unsigned long port = base + info->dataoffset;
+	ide_hwif_t *hwif;
+
+	hwif = icside_find_hwif(base);
+	if (hwif) {
+		int i;
+
+		memset(&hwif->hw, 0, sizeof(hw_regs_t));
+
+		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+			hwif->hw.io_ports[i] = (ide_ioreg_t)port;
+			port += 1 << info->stepping;
+		}
+		hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+		hwif->hw.irq  = irq;
+		hwif->hw.dma  = NO_DMA;
+		hwif->noprobe = 0;
+		hwif->chipset = ide_acorn;
+	}
+
+	return hwif;
+}
+
+static int icside_register_v5(struct expansion_card *ec, int autodma)
+{
+	unsigned long slot_port;
+	ide_hwif_t *hwif;
+
+	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 *)slot_port;
+	ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v5;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
+
+	hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
+
+	return hwif ? 0 : -1;
+}
+
+static int icside_register_v6(struct expansion_card *ec, int autodma)
+{
+	unsigned long slot_port, port;
+	ide_hwif_t *hwif, *mate;
+	int sel = 0;
+
+	slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+	port      = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+
+	if (port == 0)
+		port = slot_port;
+	else
+		sel = 1 << 5;
+
+	outb(sel, 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);
+
+	hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
+	mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+	if (ec->dma != NO_DMA) {
+		if (request_dma(ec->dma, hwif->name))
+			goto no_dma;
+
+		if (hwif) {
+			hwif->config_data = slot_port;
+			hwif->select_data = sel;
+			hwif->hw.dma  = ec->dma;
+			hwif->hw.priv = (void *)
+					(port + ICS_ARCIN_V6_INTRSTAT_1);
+			hwif->channel = 0;
+			icside_setup_dma(hwif, autodma);
+		}
+		if (mate) {
+			mate->config_data = slot_port;
+			mate->select_data = sel | 1;
+			mate->hw.dma  = ec->dma;
+			mate->hw.priv = (void *)
+					(port + ICS_ARCIN_V6_INTRSTAT_2);
+			mate->channel = 1;
+			icside_setup_dma(mate, autodma);
+		}
+	}
+#endif
+
+no_dma:
+	return hwif || mate ? 0 : -1;
+}
+
+int icside_init(void)
+{
+	int autodma = 0;
+
+#ifdef CONFIG_IDEDMA_ICS_AUTO
+	autodma = 1;
+#endif
+
+	ecard_startfind ();
+
+	do {
+		struct expansion_card *ec;
+		int result;
+
+		ec = ecard_find(0, icside_cids);
+		if (ec == NULL)
+			break;
+
+		ecard_claim(ec);
+
+		switch (icside_identifyif(ec)) {
+		case ics_if_arcin_v5:
+			result = icside_register_v5(ec, autodma);
+			break;
+
+		case ics_if_arcin_v6:
+			result = icside_register_v6(ec, autodma);
+			break;
+
+		default:
+			result = -1;
+			break;
+		}
+
+		if (result)
+			ecard_release(ec);
+	} while (1);
+
+	return 0;
+}
diff -u --recursive --new-file v2.3.6/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.3.6/linux/drivers/block/ll_rw_blk.c	Wed Jun  2 22:21:51 1999
+++ linux/drivers/block/ll_rw_blk.c	Wed Jun 16 19:26:27 1999
@@ -603,7 +603,7 @@
 
 	/* Verify requested block sizes.  */
 	for (i = 0; i < nr; i++) {
-		if (bh[i] && bh[i]->b_size != correct_size) {
+		if (bh[i]->b_size != correct_size) {
 			printk(KERN_NOTICE "ll_rw_block: device %s: "
 			       "only %d-char blocks implemented (%lu)\n",
 			       kdevname(bh[0]->b_dev),
diff -u --recursive --new-file v2.3.6/linux/drivers/block/rapide.c linux/drivers/block/rapide.c
--- v2.3.6/linux/drivers/block/rapide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/rapide.c	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,92 @@
+/*
+ * linux/arch/arm/drivers/block/ide-rapide.c
+ *
+ * Copyright (c) 1996-1998 Russell King.
+ *
+ * Changelog:
+ *  08-06-1996	RMK	Created
+ *  13-04-1998	RMK	Added manufacturer and product IDs
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+
+#include <asm/ecard.h>
+
+static const card_ids rapide_cids[] = {
+	{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
+	{ 0xffff, 0xffff }
+};
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS];
+
+static inline int rapide_register(struct expansion_card *ec)
+{
+	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
+	hw_regs_t hw;
+
+	int i;
+
+	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)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		ec[i] = NULL;
+
+	ecard_startfind();
+
+	for (i = 0; ; i++) {
+		if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
+			break;
+
+		ecard_claim(ec[i]);
+		result[i] = rapide_register(ec[i]);
+	}
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i] && result[i] < 0) {
+			ecard_release(ec[i]);
+			ec[i] = NULL;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void)
+{
+	return rapide_init();
+}
+
+void cleanup_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i]) {
+			unsigned long port;
+			port = ecard_address(ec[i], ECARD_MEMC, 0);
+
+			ide_unregister_port(port, ec[i]->irq, 16);
+			ecard_release(ec[i]);
+			ec[i] = NULL;
+		}
+}
+#endif
+
diff -u --recursive --new-file v2.3.6/linux/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c
--- v2.3.6/linux/drivers/char/n_hdlc.c	Wed May 12 13:27:37 1999
+++ linux/drivers/char/n_hdlc.c	Wed Jun 16 19:26:27 1999
@@ -9,6 +9,7 @@
  *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
  *
  * Original release 01/11/99
+ * ==FILEDATE 19990524==
  *
  * This code is released under the GNU General Public License (GPL)
  *
@@ -72,7 +73,7 @@
  */
 
 #define HDLC_MAGIC 0x239e
-#define HDLC_VERSION "1.0"
+#define HDLC_VERSION "1.2"
 
 #include <linux/version.h>
 #include <linux/config.h>
@@ -813,6 +814,8 @@
 {
 	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
 	int error = 0;
+	int count;
+	unsigned long flags;
 	
 	if (debuglevel >= DEBUG_LEVEL_INFO)	
 		printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
@@ -824,21 +827,29 @@
 
 	switch (cmd) {
 	case FIONREAD:
-		{
-			/* report count of read data available */
-			/* in next available frame (if any) */
-			int count;
-			unsigned long flags;
-			spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
-			if (n_hdlc->rx_buf_list.head)
-				count = n_hdlc->rx_buf_list.head->count;
-			else
-				count = 0;
-			spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
-			PUT_USER (error, count, (int *) arg);
-		}
+		/* report count of read data available */
+		/* in next available frame (if any) */
+		spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
+		if (n_hdlc->rx_buf_list.head)
+			count = n_hdlc->rx_buf_list.head->count;
+		else
+			count = 0;
+		spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
+		PUT_USER (error, count, (int *) arg);
 		break;
-		
+
+	case TIOCOUTQ:
+		/* get the pending tx byte count in the driver */
+		count = tty->driver.chars_in_buffer ?
+				tty->driver.chars_in_buffer(tty) : 0;
+		/* add size of next output frame in queue */
+		spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
+		if (n_hdlc->tx_buf_list.head)
+			count += n_hdlc->tx_buf_list.head->count;
+		spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
+		PUT_USER (error, count, (int*)arg);
+		break;
+
 	default:
 		error = n_tty_ioctl (tty, file, cmd, arg);
 		break;
diff -u --recursive --new-file v2.3.6/linux/drivers/char/synclink.c linux/drivers/char/synclink.c
--- v2.3.6/linux/drivers/char/synclink.c	Wed May 12 13:27:37 1999
+++ linux/drivers/char/synclink.c	Wed Jun 16 19:26:27 1999
@@ -1,6 +1,8 @@
 /*
  * linux/drivers/char/synclink.c
  *
+ * ==FILEDATE 19990610==
+ *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
  *
@@ -43,14 +45,15 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
- 
+
 #define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
 #define BREAKPOINT() asm("   int $3");
 
 #define MAX_ISA_DEVICES 10
 
-#include <linux/config.h>
+#include <linux/config.h>	
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -68,7 +71,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+#if LINUX_VERSION_CODE >= VERSION(2,1,0) 
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <asm/serial.h>
@@ -209,8 +212,21 @@
 } BH_EVENT, *BH_QUEUE;     /* Queue of BH actions to be done.  */
 
 #define MAX_BH_QUEUE_ENTRIES 200
+#define IO_PIN_SHUTDOWN_LIMIT (MAX_BH_QUEUE_ENTRIES/4)
 
 #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+struct	_input_signal_events {
+	int	ri_up;	
+	int	ri_down;
+	int	dsr_up;
+	int	dsr_down;
+	int	dcd_up;
+	int	dcd_down;
+	int	cts_up;
+	int	cts_down;
+};
+
 /*
  * Device instance data structure
  */
@@ -266,6 +282,11 @@
 	int bh_running;		/* Protection from multiple */
 	int isr_overflow;
 	int bh_requested;
+	
+	int dcd_chkcount;		/* check counts to prevent */
+	int cts_chkcount;		/* too many IRQs if a signal */
+	int dsr_chkcount;		/* is floating */
+	int ri_chkcount;
 
 	char *buffer_list;		/* virtual address of Rx & Tx buffer lists */
 	unsigned long buffer_list_phys;
@@ -327,6 +348,11 @@
 	char flag_buf[HDLC_MAX_FRAME_SIZE];
 	char char_buf[HDLC_MAX_FRAME_SIZE];	
 	BOOLEAN drop_rts_on_tx_done;
+
+	BOOLEAN loopmode_insert_requested;
+	BOOLEAN	loopmode_send_done_requested;
+	
+	struct	_input_signal_events	input_signal_events;
 };
 
 #define MGSL_MAGIC 0x5401
@@ -712,6 +738,13 @@
 
 void mgsl_tx_timeout(unsigned long context);
 
+
+void usc_loopmode_cancel_transmit( struct mgsl_struct * info );
+void usc_loopmode_insert_request( struct mgsl_struct * info );
+int usc_loopmode_active( struct mgsl_struct * info);
+void usc_loopmode_send_done( struct mgsl_struct * info );
+int usc_loopmode_send_active( struct mgsl_struct * info );
+
 /*
  * Defines a BUS descriptor value for the PCI adapter
  * local bus address ranges.
@@ -820,7 +853,8 @@
 static int mgsl_txenable(struct mgsl_struct * info, int enable);
 static int mgsl_txabort(struct mgsl_struct * info);
 static int mgsl_rxenable(struct mgsl_struct * info, int enable);
-static int mgsl_wait_event(struct mgsl_struct * info, int mask);
+static int mgsl_wait_event(struct mgsl_struct * info, int * mask);
+static int mgsl_loopmode_send_done( struct mgsl_struct * info );
 
 #define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
 
@@ -865,7 +899,7 @@
 #endif
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "1.00";
+static char *driver_version = "1.7";
 
 static struct tty_driver serial_driver, callout_driver;
 static int serial_refcount;
@@ -1001,6 +1035,7 @@
 
 	/* As a safety measure, mark the end of the chain with a NULL */
 	info->free_bh_queue_tail->link = NULL;
+	info->isr_overflow=0;
 
 }	/* end of mgsl_format_bh_queue() */
 
@@ -1092,6 +1127,14 @@
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
 		return 1;
 	}
+	
+	if ( info->isr_overflow ) {
+		if (debug_level >= DEBUG_LEVEL_BH)
+			printk("ISR overflow cleared.\n");
+		info->isr_overflow=0;
+		usc_EnableMasterIrqBit(info);
+		usc_EnableDmaInterrupts(info,DICR_MASTER);
+	}
 
 	/* Mark BH routine as complete */
 	info->bh_running   = 0;
@@ -1155,10 +1198,6 @@
 		}
 	}
 
-	if ( info->isr_overflow ) {
-		printk("ISR overflow detected.\n");
-	}
-
 	if ( debug_level >= DEBUG_LEVEL_BH )
 		printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
 			__FILE__,__LINE__,info->device_name);
@@ -1199,6 +1238,7 @@
 void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount )
 {
 	struct tty_struct *tty = info->tty;
+	unsigned long flags;
 	
 	if ( debug_level >= DEBUG_LEVEL_BH )
 		printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n",
@@ -1215,7 +1255,15 @@
 		}
 		wake_up_interruptible(&tty->write_wait);
 	}
-	
+
+	/* if transmitter idle and loopmode_send_done_requested
+	 * then start echoing RxD to TxD
+	 */
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	if ( !info->tx_active && info->loopmode_send_done_requested )
+ 		usc_loopmode_send_done( info );
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
 }	/* End Of mgsl_bh_transmit_data() */
 
 /* mgsl_bh_status_handler()
@@ -1240,6 +1288,23 @@
 		printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n",
 			__FILE__,__LINE__,info->device_name);
 
+	if (status & MISCSTATUS_RI_LATCHED) {
+		if (info->ri_chkcount)
+			(info->ri_chkcount)--;
+	}
+	if (status & MISCSTATUS_DSR_LATCHED) {
+		if (info->dsr_chkcount)
+			(info->dsr_chkcount)--;
+	}
+	if (status & MISCSTATUS_DCD_LATCHED) {
+		if (info->dcd_chkcount)
+			(info->dcd_chkcount)--;
+	}
+	if (status & MISCSTATUS_CTS_LATCHED) {
+		if (info->cts_chkcount)
+			(info->cts_chkcount)--;
+	}
+	
 }	/* End Of mgsl_bh_status_handler() */
 
 /* mgsl_isr_receive_status()
@@ -1259,8 +1324,21 @@
 		printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
 			__FILE__,__LINE__,status);
 			
-	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
-	usc_UnlatchRxstatusBits( info, status );
+ 	if ( (status & RXSTATUS_ABORT_RECEIVED) && 
+		info->loopmode_insert_requested &&
+ 		usc_loopmode_active(info) )
+ 	{
+		++info->icount.rxabort;
+	 	info->loopmode_insert_requested = FALSE;
+ 
+ 		/* clear CMR:13 to start echoing RxD to TxD */
+		info->cmr_value &= ~BIT13;
+ 		usc_OutReg(info, CMR, info->cmr_value);
+ 
+		/* disable received abort irq (no longer required) */
+	 	usc_OutReg(info, RICR,
+ 			(usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED));
+ 	}
 
 	if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
 		if (status & RXSTATUS_EXITED_HUNT)
@@ -1278,6 +1356,9 @@
 		usc_RTCmd( info, RTCmd_PurgeRxFifo );
 	}
 
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+	usc_UnlatchRxstatusBits( info, status );
+
 }	/* end of mgsl_isr_receive_status() */
 
 /* mgsl_isr_transmit_status()
@@ -1300,7 +1381,7 @@
 	
 	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
 	usc_UnlatchTxstatusBits( info, status );
-
+ 
 	if ( status & TXSTATUS_EOF_SENT )
 		info->icount.txok++;
 	else if ( status & TXSTATUS_UNDERRUN )
@@ -1356,12 +1437,32 @@
 	              MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
 		icount = &info->icount;
 		/* update input line counters */
-		if (status & MISCSTATUS_RI_LATCHED)
+		if (status & MISCSTATUS_RI_LATCHED) {
+			if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_RI);
 			icount->rng++;
-		if (status & MISCSTATUS_DSR_LATCHED)
+			if ( status & MISCSTATUS_RI )
+				info->input_signal_events.ri_up++;	
+			else
+				info->input_signal_events.ri_down++;	
+		}
+		if (status & MISCSTATUS_DSR_LATCHED) {
+			if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_DSR);
 			icount->dsr++;
+			if ( status & MISCSTATUS_DSR )
+				info->input_signal_events.dsr_up++;
+			else
+				info->input_signal_events.dsr_down++;
+		}
 		if (status & MISCSTATUS_DCD_LATCHED) {
+			if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_DCD);
 			icount->dcd++;
+			if ( status & MISCSTATUS_DCD )
+				info->input_signal_events.dcd_up++;
+			else
+				info->input_signal_events.dcd_down++;
 #ifdef CONFIG_HARD_PPS
 			if ((info->flags & ASYNC_HARDPPS_CD) &&
 			    (status & MISCSTATUS_DCD_LATCHED))
@@ -1369,7 +1470,15 @@
 #endif
 		}
 		if (status & MISCSTATUS_CTS_LATCHED)
+		{
+			if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_CTS);
 			icount->cts++;
+			if ( status & MISCSTATUS_CTS )
+				info->input_signal_events.cts_up++;
+			else
+				info->input_signal_events.cts_down++;
+		}
 		wake_up_interruptible(&info->status_event_wait_q);
 		wake_up_interruptible(&info->event_wait_q);
 
@@ -1411,6 +1520,8 @@
 		}
 	}
 
+	mgsl_bh_queue_put(info, BH_TYPE_STATUS, status);
+	
 	/* for diagnostics set IRQ flag */
 	if ( status & MISCSTATUS_TXC_LATCHED ){
 		usc_OutReg( info, SICR,
@@ -1642,8 +1753,10 @@
 	/* Post a receive event for BH processing. */
 	mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status );
 	
-	if ( status & BIT3 )
+	if ( status & BIT3 ) {
 		info->rx_overflow = 1;
+		info->icount.buf_overrun++;
+	}
 
 }	/* end of mgsl_isr_receive_dma() */
 
@@ -1696,9 +1809,9 @@
 		if ( info->isr_overflow ) {
 			printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n",
 				__FILE__,__LINE__,info->device_name, irq);
-				/* Interrupt overflow. Reset adapter and exit. */
-//				UscReset(info);
-//				break;
+			usc_DisableMasterIrqBit(info);
+			usc_DisableDmaInterrupts(info,DICR_MASTER);
+			break;
 		}
 	}
 	
@@ -1980,6 +2093,11 @@
 		usc_set_async_mode(info);
 		
 	usc_set_serial_signals(info);
+	
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
 
 	/* enable modem signal IRQs and read initial signal states */
 	usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);		
@@ -2112,16 +2230,27 @@
 
 	if ( info->params.mode == MGSL_MODE_HDLC ) {
 		/* operating in synchronous (frame oriented) mode */
-	
+
 		if (info->tx_active) {
 			ret = 0; goto cleanup; 
 		}
-		
+	
+		/* if operating in HDLC LoopMode and the adapter  */
+		/* has yet to be inserted into the loop, we can't */
+		/* transmit					  */
+
+		if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) &&
+			!usc_loopmode_active(info) )
+		{
+			ret = 0;
+			goto cleanup;
+		}
+
 		if ( info->xmit_cnt ) {
 			/* Send accumulated from send_char() calls */
 			/* as frame and wait before accepting more data. */
 			ret = 0;
-				
+			
 			/* copy data from circular xmit_buf to */
 			/* transmit DMA buffer. */
 			mgsl_load_tx_dma_buffer(info,
@@ -2578,8 +2707,19 @@
 			
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	if ( enable ) {
-		if ( !info->tx_enabled )
+		if ( !info->tx_enabled ) {
+
 			usc_start_transmitter(info);
+			/*--------------------------------------------------
+			 * if HDLC/SDLC Loop mode, attempt to insert the
+			 * station in the 'loop' by setting CMR:13. Upon
+			 * receipt of the next GoAhead (RxAbort) sequence,
+			 * the OnLoop indicator (CCSR:7) should go active
+			 * to indicate that we are on the loop
+			 *--------------------------------------------------*/
+			if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+				usc_loopmode_insert_request( info );
+		}
 	} else {
 		if ( info->tx_enabled )
 			usc_stop_transmitter(info);
@@ -2604,7 +2744,12 @@
 			
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC )
-		usc_TCmd(info,TCmd_SendAbort);
+	{
+		if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+			usc_loopmode_cancel_transmit( info );
+		else
+			usc_TCmd(info,TCmd_SendAbort);
+	}
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	return 0;
 	
@@ -2640,25 +2785,39 @@
 /* mgsl_wait_event() 	wait for specified event to occur
  * 	
  * Arguments:	 	info	pointer to device instance data
- * 			mask	bitmask of events to wait for
- * Return Value:	bit mask of triggering event, otherwise error code
+ * 			mask	pointer to bitmask of events to wait for
+ * Return Value:	0 	if successful and bit mask updated with
+ *				of events triggerred,
+ * 			otherwise error code
  */
-static int mgsl_wait_event(struct mgsl_struct * info, int mask)
+static int mgsl_wait_event(struct mgsl_struct * info, int * mask_ptr)
 {
  	unsigned long flags;
 	int s;
 	int rc=0;
 	u16 regval;
 	struct mgsl_icount cprev, cnow;
+	int events = 0;
+	int mask;
+	struct	_input_signal_events signal_events_prev, signal_events_now;
+
+	COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+	if (rc) {
+		return  -EFAULT;
+	}
 		 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__,
 			info->device_name, mask);
-			
+
 	spin_lock_irqsave(&info->irq_spinlock,flags);
-	
+
+	usc_get_serial_signals(info);
+	s = info->serial_signals;
+
 	/* note the counters on entry */
 	cprev = info->icount;
+	signal_events_prev = info->input_signal_events;
 	
 	if (mask & MgslEvent_ExitHuntMode) {
 		/* enable exit hunt mode IRQ */
@@ -2676,7 +2835,22 @@
 	
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-	while(!rc) {
+	/* Determine if any user requested events for input signals is currently TRUE */
+	
+	events |= (mask & ((s & SerialSignal_DSR) ?
+			MgslEvent_DsrActive:MgslEvent_DsrInactive));
+
+	events |= (mask & ((s & SerialSignal_DCD) ?
+			MgslEvent_DcdActive:MgslEvent_DcdInactive));
+		
+	events |= (mask & ((s & SerialSignal_CTS) ?
+			MgslEvent_CtsActive:MgslEvent_CtsInactive));
+		
+	events |= (mask & ((s & SerialSignal_RI) ?
+			MgslEvent_RiActive:MgslEvent_RiInactive));
+	
+
+	while(!events) {
 		/* sleep until event occurs */
 		interruptible_sleep_on(&info->event_wait_q);
 		
@@ -2687,39 +2861,52 @@
 		}
 			
 		spin_lock_irqsave(&info->irq_spinlock,flags);
+
 		/* get icount and serial signal states */
 		cnow = info->icount;
-		s = info->serial_signals;
+		signal_events_now = info->input_signal_events;
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+		if (signal_events_now.dsr_up != signal_events_prev.dsr_up && 
+				mask & MgslEvent_DsrActive )
+			events |= MgslEvent_DsrActive;
+		
+		if (signal_events_now.dsr_down != signal_events_prev.dsr_down && 
+				mask & MgslEvent_DsrInactive )
+			events |= MgslEvent_DsrInactive;
+
+		if (signal_events_now.dcd_up != signal_events_prev.dcd_up &&
+				mask & MgslEvent_DcdActive )
+			events |= MgslEvent_DcdActive;
+		
+		if (signal_events_now.dcd_down != signal_events_prev.dcd_down &&
+				mask & MgslEvent_DcdInactive )
+			events |= MgslEvent_DcdInactive;
+		
+		if (signal_events_now.cts_up != signal_events_prev.cts_up &&
+				mask & MgslEvent_CtsActive )
+			events |= MgslEvent_CtsActive;
+		
+		if (signal_events_now.cts_down != signal_events_prev.cts_down &&
+				mask & MgslEvent_CtsInactive )
+			events |= MgslEvent_CtsInactive;
+		
+		if (signal_events_now.ri_up != signal_events_prev.ri_up &&
+				mask & MgslEvent_RiActive )
+			events |= MgslEvent_RiActive;
+		
+		if (signal_events_now.ri_down != signal_events_prev.ri_down &&
+				mask & MgslEvent_RiInactive )
+			events |= MgslEvent_RiInactive;
 		
-		rc = 0;		
-		
-		if (cnow.dsr != cprev.dsr)
-			rc |= (mask & ((s & SerialSignal_DSR) ?
-				MgslEvent_DsrActive:MgslEvent_DsrInactive));
-		
-		if (cnow.dcd != cprev.dcd)
-			rc |= (mask & ((s & SerialSignal_DCD) ?
-				MgslEvent_DcdActive:MgslEvent_DcdInactive));
-				
-		if (cnow.cts != cprev.cts)
-			rc |= (mask & ((s & SerialSignal_CTS) ?
-				MgslEvent_CtsActive:MgslEvent_CtsInactive));
-				
-		if (cnow.rng != cprev.rng)
-			rc |= (mask & ((s & SerialSignal_RI) ?
-				MgslEvent_RiActive:MgslEvent_RiInactive));
-				
 		if (cnow.exithunt != cprev.exithunt)
-			rc |= (mask & MgslEvent_ExitHuntMode);
-			
+			events |= (mask & MgslEvent_ExitHuntMode);
+
 		if (cnow.rxidle != cprev.rxidle)
-			rc |= (mask & MgslEvent_ExitHuntMode);
-				
-		if (!rc)
-			rc = -EIO; /* no change => error */
-			
+			events |= (mask & MgslEvent_IdleReceived);
+		
 		cprev = cnow;
+		signal_events_prev = signal_events_now;
 	}
 	
 	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
@@ -2732,7 +2919,10 @@
 		}
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	}
-	
+
+	if ( rc == 0 )
+		PUT_USER(rc, events, mask_ptr);
+		
 	return rc;
 	
 }	/* end of mgsl_wait_event() */
@@ -2772,7 +2962,7 @@
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_get_modem_info %s value=%08X\n",
-			 __FILE__,__LINE__, info->device_name, *value );
+			 __FILE__,__LINE__, info->device_name, result );
 			
 	PUT_USER(err,result,value);
 	return err;
@@ -2928,7 +3118,9 @@
 		case MGSL_IOCGSTATS:
 			return mgsl_get_stats(info,(struct mgsl_icount*)arg);
 		case MGSL_IOCWAITEVENT:
-			return mgsl_wait_event(info,(int)arg);
+			return mgsl_wait_event(info,(int*)arg);
+		case MGSL_IOCLOOPTXDONE:
+			return mgsl_loopmode_send_done(info);
 		case MGSL_IOCCLRMODCOUNT:
 			while(MOD_IN_USE)
 				MOD_DEC_USE_COUNT;
@@ -3626,11 +3818,6 @@
 	}
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0)
-	ret += sprintf(buf+ret, "irq_spinlock=%08X\n",
-	 		info->irq_spinlock.lock );
-#endif
-	
 	return ret;
 	
 }	/* end of line_info() */
@@ -4227,6 +4414,18 @@
 			if ( PCIBIOS_SUCCESSFUL == pcibios_find_device(
 				MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) {
 				
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+				struct pci_dev *pdev = pci_find_slot(bus,func);
+				irq_line = pdev->irq;				
+#else												
+				if (pcibios_read_config_byte(bus,func,
+					PCI_INTERRUPT_LINE,&irq_line) ) {
+					printk( "%s(%d):USC I/O addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+#endif
+
 				if (pcibios_read_config_dword(bus,func,
 					PCI_BASE_ADDRESS_3,&shared_mem_base) ) {
 					printk( "%s(%d):Shared mem addr not set.\n",
@@ -4248,13 +4447,6 @@
 					continue;
 				}
 				
-				if (pcibios_read_config_byte(bus,func,
-					PCI_INTERRUPT_LINE,&irq_line) ) {
-					printk( "%s(%d):USC I/O addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
-				
 				info = mgsl_allocate_device();
 				if ( !info ) {
 					/* error allocating device instance data */
@@ -4671,29 +4863,53 @@
 {
 	u16 RegValue;
 
-	/* Channel mode Register (CMR)
-	 *
-	 * <15..14>  00    Tx Sub modes, Underrun Action
-	 * <13>      0     1 = Send Preamble before opening flag
-	 * <12>      0     1 = Consecutive Idles share common 0
-	 * <11..8>   0110  Transmitter mode = HDLC/SDLC
-	 * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
-	 * <3..0>    0110  Receiver mode = HDLC/SDLC
-	 *
-	 * 0000 0110 0000 0110 = 0x0606
-	 */
+ 	if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+ 	{
+ 	   /*
+ 	   ** Channel Mode Register (CMR)
+ 	   **
+ 	   ** <15..14>    10    Tx Sub Modes, Send Flag on Underrun
+ 	   ** <13>        0     0 = Transmit Disabled (initially)
+ 	   ** <12>        0     1 = Consecutive Idles share common 0
+ 	   ** <11..8>     1110  Transmitter Mode = HDLC/SDLC Loop
+ 	   ** <7..4>      0000  Rx Sub Modes, addr/ctrl field handling
+ 	   ** <3..0>      0110  Receiver Mode = HDLC/SDLC
+ 	   **
+ 	   ** 1000 1110 0000 0110 = 0x8e06
+ 	   */
+ 	   RegValue = 0x8e06;
+ 
+ 	   /*--------------------------------------------------
+ 	    * ignore user options for UnderRun Actions and
+ 	    * preambles
+ 	    *--------------------------------------------------*/
+ 	}
+ 	else
+ 	{	
+		/* Channel mode Register (CMR)
+		 *
+		 * <15..14>  00    Tx Sub modes, Underrun Action
+		 * <13>      0     1 = Send Preamble before opening flag
+		 * <12>      0     1 = Consecutive Idles share common 0
+		 * <11..8>   0110  Transmitter mode = HDLC/SDLC
+		 * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
+		 * <3..0>    0110  Receiver mode = HDLC/SDLC
+		 *
+		 * 0000 0110 0000 0110 = 0x0606
+		 */
 
-	RegValue = 0x0606;
+		RegValue = 0x0606;
 
-	if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
-		RegValue |= BIT14;
-	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
-		RegValue |= BIT15;
-	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
-		RegValue |= BIT15 + BIT14;
+		if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
+			RegValue |= BIT14;
+		else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
+			RegValue |= BIT15;
+		else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
+			RegValue |= BIT15 + BIT14;
 
-	if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
-		RegValue |= BIT13;
+		if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
+			RegValue |= BIT13;
+	}
 
 	if ( info->params.flags & HDLC_FLAG_SHARE_ZERO )
 		RegValue |= BIT12;
@@ -4862,6 +5078,8 @@
 		RegValue |= 0x0003;	/* RxCLK from DPLL */
 	else if ( info->params.flags & HDLC_FLAG_RXC_BRG )
 		RegValue |= 0x0004;	/* RxCLK from BRG0 */
+ 	else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN)
+ 		RegValue |= 0x0006;	/* RxCLK from TXC Input */
 	else
 		RegValue |= 0x0007;	/* RxCLK from Port1 */
 
@@ -4869,6 +5087,8 @@
 		RegValue |= 0x0018;	/* TxCLK from DPLL */
 	else if ( info->params.flags & HDLC_FLAG_TXC_BRG )
 		RegValue |= 0x0020;	/* TxCLK from BRG0 */
+ 	else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN)
+ 		RegValue |= 0x0038;	/* RxCLK from TXC Input */
 	else
 		RegValue |= 0x0030;	/* TxCLK from Port0 */
 
@@ -4922,10 +5142,24 @@
 		/*  of rounding up and then subtracting 1 we just don't subtract */
 		/*  the one in this case. */
 
-		Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
-		if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
-		       / info->params.clock_speed) )
-			Tc--;
+ 		/*--------------------------------------------------
+ 		 * ejz: for DPLL mode, application should use the
+ 		 * same clock speed as the partner system, even 
+ 		 * though clocking is derived from the input RxData.
+ 		 * In case the user uses a 0 for the clock speed,
+ 		 * default to 0xffffffff and don't try to divide by
+ 		 * zero
+ 		 *--------------------------------------------------*/
+ 		if ( info->params.clock_speed )
+ 		{
+			Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
+			if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
+			       / info->params.clock_speed) )
+				Tc--;
+ 		}
+ 		else
+ 			Tc = -1;
+ 				  
 
 		/* Write 16-bit Time Constant for BRG1 */
 		usc_OutReg( info, TC1R, Tc );
@@ -6328,6 +6562,13 @@
 	if ( debug_level >= DEBUG_LEVEL_DATA )
 		mgsl_trace_block(info,Buffer,BufferSize,1);	
 
+	if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+		/* set CMR:13 to start transmit when
+		 * next GoAhead (abort) is received
+		 */
+	 	info->cmr_value |= BIT13;			  
+	}
+		
 	/* Setup the status and RCC (Frame Size) fields of the 1st */
 	/* buffer entry in the transmit DMA buffer list. */
 
@@ -6381,7 +6622,7 @@
 	unsigned int i;
 	BOOLEAN rc = TRUE;
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	usc_reset(info);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
@@ -6471,7 +6712,7 @@
 	usc_reset(info);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-	if ( !info->irq_occurred )
+	if ( !info->irq_occurred ) 
 		return FALSE;
 	else
 		return TRUE;
@@ -6499,7 +6740,7 @@
 	volatile unsigned long EndTime;
 	unsigned long flags;
 	MGSL_PARAMS tmp_params;
-	
+
 	/* save current port options */
 	memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS));
 	/* load default port options */
@@ -6657,7 +6898,7 @@
 	/**********************************/
 	/* WAIT FOR TRANSMIT FIFO TO FILL */
 	/**********************************/
-															 
+	
 	/* Wait 100ms */
 	EndTime = jiffies + jiffies_from_ms(100);
 
@@ -6724,7 +6965,7 @@
 
 	if ( rc == TRUE ){
 		/* CHECK FOR TRANSMIT ERRORS */
-		if ( status & (BIT5 + BIT1) )
+		if ( status & (BIT5 + BIT1) ) 
 			rc = FALSE;
 	}
 
@@ -6981,13 +7222,90 @@
 	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
 		info->icount.txtimeout++;
 	}
-	
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	info->tx_active = 0;
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+		usc_loopmode_cancel_transmit( info );
+
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
 	mgsl_bh_transmit_data(info,0);
 	
 }	/* end of mgsl_tx_timeout() */
+
+/* signal that there are no more frames to send, so that
+ * line is 'released' by echoing RxD to TxD when current
+ * transmission is complete (or immediately if no tx in progress).
+ */
+static int mgsl_loopmode_send_done( struct mgsl_struct * info )
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+		if (info->tx_active)
+			info->loopmode_send_done_requested = TRUE;
+		else
+			usc_loopmode_send_done(info);
+	}
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	return 0;
+}
+
+/* release the line by echoing RxD to TxD
+ * upon completion of a transmit frame
+ */
+void usc_loopmode_send_done( struct mgsl_struct * info )
+{
+ 	info->loopmode_send_done_requested = FALSE;
+ 	/* clear CMR:13 to 0 to start echoing RxData to TxData */
+ 	info->cmr_value &= ~BIT13;			  
+ 	usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* abort a transmit in progress while in HDLC LoopMode
+ */
+void usc_loopmode_cancel_transmit( struct mgsl_struct * info )
+{
+ 	/* reset tx dma channel and purge TxFifo */
+ 	usc_RTCmd( info, RTCmd_PurgeTxFifo );
+ 	usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+  	usc_loopmode_send_done( info );
+}
+
+/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled
+ * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort)
+ * we must clear CMR:13 to begin repeating TxData to RxData
+ */
+void usc_loopmode_insert_request( struct mgsl_struct * info )
+{
+ 	info->loopmode_insert_requested = TRUE;
+ 
+ 	/* enable RxAbort irq. On next RxAbort, clear CMR:13 to
+ 	 * begin repeating TxData on RxData (complete insertion)
+	 */
+ 	usc_OutReg( info, RICR, 
+		(usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) );
+		
+	/* set CMR:13 to insert into loop on next GoAhead (RxAbort) */
+	info->cmr_value |= BIT13;
+ 	usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* return 1 if station is inserted into the loop, otherwise 0
+ */
+int usc_loopmode_active( struct mgsl_struct * info)
+{
+ 	return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ;
+}
+
+/* return 1 if USC is in loop send mode, otherwise 0
+ */
+int usc_loopmode_send_active( struct mgsl_struct * info )
+{
+	return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ;
+}			  
 
diff -u --recursive --new-file v2.3.6/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.3.6/linux/drivers/char/tty_io.c	Tue May 11 14:37:40 1999
+++ linux/drivers/char/tty_io.c	Wed Jun 16 19:26:27 1999
@@ -651,9 +651,7 @@
 	ssize_t ret = 0, written = 0;
 	struct inode *inode = file->f_dentry->d_inode;
 	
-	up(&inode->i_sem);
-	if (down_interruptible(&inode->i_atomic_write)) {
-		down(&inode->i_sem);
+	if (down_interruptible(&inode->i_sem)) {
 		return -ERESTARTSYS;
 	}
 	for (;;) {
@@ -678,8 +676,7 @@
 		file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 		ret = written;
 	}
-	up(&inode->i_atomic_write);
-	down(&inode->i_sem);
+	up(&inode->i_sem);
 	return ret;
 }
 
diff -u --recursive --new-file v2.3.6/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.3.6/linux/drivers/net/Makefile	Mon Jun  7 14:35:09 1999
+++ linux/drivers/net/Makefile	Thu Jun 17 01:11:35 1999
@@ -143,14 +143,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_ETHERH),y)
-CONFIG_8390_BUILTIN = y
-else
-  ifeq ($(CONFIG_ETHERH),m)
-  CONFIG_8390_MODULE = y
-  endif
-endif
-
 ifeq ($(CONFIG_WD80x3),y)
 L_OBJS += wd.o
 CONFIG_8390_BUILTIN = y
@@ -168,14 +160,6 @@
   ifeq ($(CONFIG_EL2),m)
   CONFIG_8390_MODULE = y
   M_OBJS += 3c503.o
-  endif
-endif
-
-ifeq ($(CONFIG_ETHERH),y)
-CONFIG_8390_BUILTIN = y
-else
-  ifeq ($(CONFIG_ETHERH),m)
-  CONFIG_8390_MODULE = y
   endif
 endif
 
diff -u --recursive --new-file v2.3.6/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.3.6/linux/drivers/net/ibmtr.c	Wed May 12 13:27:37 1999
+++ linux/drivers/net/ibmtr.c	Wed Jun 16 19:26:27 1999
@@ -515,7 +515,7 @@
 	/* How much shared RAM is on adapter ? */
 #ifdef PCMCIA
 	ti->avail_shared_ram = pcmcia_reality_check(get_sram_size(ti));
-	ibmtr_mem_base = ti->sram_base ; 
+	ibmtr_mem_base = ti->sram_base << 12 ; 
 #else
 	ti->avail_shared_ram = get_sram_size(ti);
 #endif
@@ -835,6 +835,9 @@
 			(int)readb(ti->srb + offsetof(struct srb_close_adapter, ret_code)));
 
         dev->start = 0;
+#ifdef PCMCIA
+	ti->sram = 0 ;
+#endif
 	DPRINTK("Adapter closed.\n");
 	MOD_DEC_USE_COUNT;
 
diff -u --recursive --new-file v2.3.6/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- v2.3.6/linux/drivers/net/irda/irport.c	Mon Jun  7 16:18:58 1999
+++ linux/drivers/net/irda/irport.c	Wed Jun 16 19:26:27 1999
@@ -321,7 +321,7 @@
 	/* Turn on interrups */
 	outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER);
 
-	spin_unlock_irqrestore(&self->lock, flags);
+	spin_unlock_irqrestore(&idev->lock, flags);
 }
 
 /*
diff -u --recursive --new-file v2.3.6/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.3.6/linux/drivers/pci/pci.c	Sun Mar  7 15:19:55 1999
+++ linux/drivers/pci/pci.c	Sat Jun 19 18:20:13 1999
@@ -47,13 +47,23 @@
 struct pci_dev *
 pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
 {
-	if (!from)
-		from = pci_devices;
-	else
-		from = from->next;
-	while (from && (from->vendor != vendor || from->device != device))
-		from = from->next;
-	return from;
+	struct pci_dev *next;
+
+	next = pci_devices;
+	if (from)
+		next = from->next;
+
+	while (next) {
+		struct pci_dev *dev = next;
+		next = next->next;
+		if (vendor != PCI_ANY_ID && dev->vendor != vendor)
+			continue;
+		if (device != PCI_ANY_ID && dev->device != device)
+			continue;
+
+		return dev;
+	}
+	return NULL;
 }
 
 
@@ -178,10 +188,8 @@
 
 		if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) ||
 		    /* some broken boards return 0 if a slot is empty: */
-		    l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) {
-			is_multi = 0;
+		    l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
 			continue;
-		}
 
 		dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
 		memset(dev, 0, sizeof(*dev));
diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h
--- v2.3.6/linux/drivers/sbus/audio/cs4215.h	Mon Mar 15 16:11:30 1999
+++ linux/drivers/sbus/audio/cs4215.h	Thu Jun 17 01:08:50 1999
@@ -11,11 +11,10 @@
 struct cs4215 {
 	__u8	data[4];	/* Data mode: Time slots 5-8 */
 	__u8	ctrl[4];	/* Ctrl mode: Time slots 1-4 */
-	__volatile__ struct dbri_mem td;
-	__volatile__ struct dbri_mem rd;
 	__u8	onboard;
-	__u32	status;
-        __u32	version;
+	__u8	offset;		/* Bit offset from frame sync to time slot 1 */
+	volatile __u32	status;
+	volatile __u32	version;
 };
 
 
diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c
--- v2.3.6/linux/drivers/sbus/audio/dbri.c	Mon Mar 15 16:11:30 1999
+++ linux/drivers/sbus/audio/dbri.c	Thu Jun 17 01:08:50 1999
@@ -79,25 +79,25 @@
 #define D_CMD	(1<<2)
 #define D_MM	(1<<3)
 #define D_USR	(1<<4)
+#define D_DESC	(1<<5)
 
-/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */
 static int dbri_debug = 0;
 MODULE_PARM(dbri_debug, "i");
 
+static int dbri_trace = 0;
+MODULE_PARM(dbri_trace, "i");
+#define tprintk(x) if(dbri_trace) printk x
+
 static char *cmds[] = { 
   "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
   "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
 };
 
-/* Bit hunting */
-#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->dma->cmd[i]); }
-
 #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
 
 #else
 
 #define dprintk(a, x)
-#define dumpcmd
 #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
 
 #endif	/* DBRI_DEBUG */
@@ -114,41 +114,42 @@
 ****************************************************************************
 ************** DBRI initialization and command synchronization *************
 ****************************************************************************
-*/
 
+Commands are sent to the DBRI by building a list of them in memory,
+then writing the address of the first list item to DBRI register 8.
+The list is terminated with a WAIT command, which can generate a
+CPU interrupt if required.
+
+Since the DBRI can run in parallel with the CPU, several means of
+synchronization present themselves.  The original scheme (Rudolf's)
+was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+an interrupt signaled completion, and wait on a wait_queue if a routine
+attempted to cmdlock while the flag was set.  The problems arose when
+we tried to cmdlock from inside an interrupt handler, which might
+cause scheduling in an interrupt (if we waited), etc, etc
+
+A more sophisticated scheme might involve a circular command buffer
+or an array of command buffers.  A routine could fill one with
+commands and link it onto a list.  When a interrupt signaled
+completion of the current command buffer, look on the list for
+the next one.
+
+I've decided to implement something much simpler - after each command,
+the CPU waits for the DBRI to finish the command by polling the P bit
+in DBRI register 0.  I've tried to implement this in such a way
+that might make implementing a more sophisticated scheme easier.
+
+Every time a routine wants to write commands to the DBRI, it must
+first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
+in return.  After the commands have been writen, dbri_cmdsend() is
+called with the final pointer value.
 
-/*
- * Commands are sent to the DBRI by building a list of them in memory,
- * then writing the address of the first list item to DBRI register 8.
- * The list is terminated with a WAIT command, which can generate a
- * CPU interrupt if required.
- *
- * Since the DBRI can run asynchronously to the CPU, several means of
- * synchronization present themselves.  The original scheme (Rudolf's)
- * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
- * an interrupt signaled completion, and wait on a wait_queue if a routine
- * attempted to cmdlock while the flag was set.  The problems arose when
- * we tried to cmdlock from inside an interrupt handler, which might
- * cause scheduling in an interrupt (if we waited), etc, etc
- *
- * A more sophisticated scheme might involve a circular command buffer
- * or an array of command buffers.  A routine could fill one with
- * commands and link it onto a list.  When a interrupt signaled
- * completion of the current command buffer, look on the list for
- * the next one.
- *
- * I've decided to implement something much simpler - after each command,
- * the CPU waits for the DBRI to finish the command by polling the P bit
- * in DBRI register 0.  I've tried to implement this in such a way
- * that might make implementing a more sophisticated scheme easier.
- *
- * Every time a routine wants to write commands to the DBRI, it must
- * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
- * in return.  After the commands have been writen, dbri_cmdsend() is
- * called with the final pointer value.
- */
+Something a little more clever is required if this code is ever run
+on an SMP machine.
+
+*/
 
-static int dbri_locked = 0;			/* XXX not SMP safe! XXX */
+static int dbri_locked = 0;
 
 static volatile int * dbri_cmdlock(struct dbri *dbri)
 {
@@ -159,9 +160,21 @@
         return dbri->dma->cmd;
 }
 
+static void dbri_process_interrupt_buffer(struct dbri *);
+
 static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd)
 {
-        int maxloops = 1000000;
+	int MAXLOOPS = 1000000;
+	int maxloops = MAXLOOPS;
+	unsigned int flags;
+	volatile int * ptr;
+
+	for (ptr = dbri->dma->cmd; ptr < cmd; ptr ++) {
+		dprintk(D_CMD, ("DBRI cmd: %08x:%08x\n",
+				(unsigned int) ptr, *ptr));
+	}
+
+	save_and_cli(flags);
 
         dbri_locked --;
         if (dbri_locked != 0) {
@@ -170,14 +183,25 @@
                 printk("DBRI: Command buffer overflow! (bug in driver)\n");
         } else {
                 *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
-                *(cmd++) = DBRI_CMD(D_WAIT, 0, 0);
+		*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+		dbri->wait_seen = 0;
                 dbri->regs->reg8 = (int)dbri->dma_dvma->cmd;
-                while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P));
+		while ((--maxloops) > 0 && (dbri->regs->reg0 & D_P));
+		if (maxloops == 0) {
+			printk("DBRI: Chip never completed command buffer\n");
+		} else {
+			while ((--maxloops) > 0 && (! dbri->wait_seen))
+				dbri_process_interrupt_buffer(dbri);
+			if (maxloops == 0) {
+				printk("DBRI: Chip never acked WAIT\n");
+			} else {
+				dprintk(D_INT, ("DBRI: Chip completed command buffer (%d)\n",
+						MAXLOOPS - maxloops));
+			}
+		}
         }
 
-        if (maxloops == 0) {
-                printk("DBRI: Chip never completed command buffer\n");
-        }
+	restore_flags(flags);
 }
 
 static void dbri_reset(struct dbri *dbri)
@@ -198,7 +222,7 @@
 	dbri_reset(dbri);
         free_irq(dbri->irq, dbri);
         sparc_free_io(dbri->regs, dbri->regs_size);
-        /* Should we release the DMA structure dbri->dma here? */
+	release_region((unsigned long) dbri->dma, sizeof(struct dbri_dma));
         kfree(dbri);
 }
 
@@ -242,6 +266,17 @@
 ****************************************************************************
 *************************** DBRI interrupt handler *************************
 ****************************************************************************
+
+The DBRI communicates with the CPU mainly via a circular interrupt
+buffer.  When an interrupt is signaled, the CPU walks through the
+buffer and calls dbri_process_one_interrupt() for each interrupt word.
+Complicated interrupts are handled by dedicated functions (which
+appear first in this file).  Any pending interrupts can be serviced by
+calling dbri_process_interrupt_buffer(), which works even if the CPU's
+interrupts are disabled.  This function is used by dbri_cmdsend()
+to make sure we're synced up with the chip after each command sequence,
+even if we're running cli'ed.
+
 */
 
 
@@ -263,6 +298,7 @@
         case 2:
                 b = ((b & 0xaaaaaaaa) >>  1) | ((b & 0x55555555) <<  1);
         case 1:
+	case 0:
                 break;
         default:
                 printk("DBRI reverse_bytes: unsupported length\n");
@@ -273,37 +309,39 @@
 /* transmission_complete_intr()
  *
  * Called by main interrupt handler when DBRI signals transmission complete
- * on a pipe.
+ * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
  *
  * Walks through the pipe's list of transmit buffer descriptors, releasing
- * each one's DMA buffer (if present) and signaling its callback routine
- * (if present), before flaging the descriptor available and proceeding
- * to the next one.
- *
- * Assumes that only the last in a chain of descriptors will have FINT
- * sent to signal an interrupt, so that the chain will be completely
- * transmitted by the time we get here, and there's no need to save
- * any of the descriptors.  In particular, use of the DBRI's CDP command
- * is precluded, but I've not been able to get CDP working reliably anyway.
+ * each one's DMA buffer (if present), flagging the descriptor available,
+ * and signaling its callback routine (if present), before proceeding
+ * to the next one.  Stops when the first descriptor is found without
+ * TBC (Transmit Buffer Complete) set, or we've run through them all.
  */
 
 static void transmission_complete_intr(struct dbri *dbri, int pipe)
 {
-        int td = dbri->pipes[pipe].desc;
+	int td;
         int status;
         void *buffer;
         void (*callback)(void *, int);
+	void *callback_arg;
 
-        dbri->pipes[pipe].desc = -1;
+	td = dbri->pipes[pipe].desc;
 
-        for (; td >= 0; td = dbri->descs[td].next) {
+	while (td >= 0) {
 
                 if (td >= DBRI_NO_DESCS) {
                         printk("DBRI: invalid td on pipe %d\n", pipe);
                         return;
                 }
 
-                status = dbri->dma->desc[td].word4;
+		status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
+
+		if (! (status & DBRI_TD_TBC)) {
+			break;
+		}
+
+		dprintk(D_INT, ("DBRI: TD %d, status 0x%02x\n", td, status));
 
                 buffer = dbri->descs[td].buffer;
                 if (buffer) {
@@ -313,12 +351,16 @@
                 }
 
                 callback = dbri->descs[td].output_callback;
-                if (callback != NULL) {
-                        callback(dbri->descs[td].output_callback_arg,
-                                 DBRI_TD_STATUS(status) & 0xe);
-                }
+		callback_arg = dbri->descs[td].output_callback_arg;
 
                 dbri->descs[td].inuse = 0;
+
+		td = dbri->descs[td].next;
+		dbri->pipes[pipe].desc = td;
+
+		if (callback != NULL) {
+			callback(callback_arg, status & 0xe);
+		}
         }
 }
 
@@ -335,7 +377,7 @@
         }
 
         dbri->descs[rd].inuse = 0;
-        dbri->pipes[pipe].desc = -1;
+	dbri->pipes[pipe].desc = dbri->descs[rd].next;
         status = dbri->dma->desc[rd].word1;
 
         buffer = dbri->descs[rd].buffer;
@@ -351,95 +393,170 @@
                          DBRI_RD_STATUS(status),
                          DBRI_RD_CNT(status)-2);
         }
+
+	dprintk(D_INT, ("DBRI: Recv RD %d, status 0x%02x, len %d\n",
+			rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)));
 }
 
-static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+static void dbri_process_one_interrupt(struct dbri *dbri, int x)
 {
-	struct dbri *dbri = (struct dbri *)opaque;
-	int x;
-	
-	/*
-	 * Read it, so the interrupt goes away.
-	 */
-	x = dbri->regs->reg1;
+	int val = D_INTR_GETVAL(x);
+	int channel = D_INTR_GETCHAN(x);
+	int command = D_INTR_GETCMD(x);
+	int code = D_INTR_GETCODE(x);
+	int rval = D_INTR_GETRVAL(x);
+
+	if (channel == D_INTR_CMD) {
+		dprintk(D_INT,("DBRI: INTR: Command: %-5s  Value:%d\n",
+			       cmds[command], val));
+	} else {
+		dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
+			       channel, code, rval));
+	}
 
-	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
-		/*
-		 * What should I do here ?
-		 */
-		if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
-		if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
-		if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
-		if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+	if (channel == D_INTR_CMD && command == D_WAIT) {
+		dbri->wait_seen ++;
 	}
 
-	if (!(x & D_IR))	/* Not for us */
-		return;
+	if (code == D_INTR_SBRI) {
 
-	x = dbri->dma->intr[dbri->dbri_irqp];
-	while (x != 0) {
-                int val = D_INTR_GETVAL(x);
-                int channel = D_INTR_GETCHAN(x);
+		/* SBRI - BRI status change */
 
-		dbri->dma->intr[dbri->dbri_irqp] = 0;
+		int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
+		dbri->liu_state = liu_states[val & 0x7];
+		if (dbri->liu_callback)
+			dbri->liu_callback(dbri->liu_callback_arg);
+	}
 
-		if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
-			dprintk(D_INT,("DBRI: INTR: Command: %-5s  Value:%d\n",
-				cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
-		} else {
-			dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
-				D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
-				D_INTR_GETRVAL(x)));
-		}
+	if (code == D_INTR_BRDY) {
+		reception_complete_intr(dbri, channel);
+	}
 
-                if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
+	if (code == D_INTR_XCMP) {
+		transmission_complete_intr(dbri, channel);
+	}
 
-                        /* SBRI - BRI status change */
+	if (code == D_INTR_UNDR) {
 
-                        int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
-                        dbri->liu_state = liu_states[val & 0x7];
-                        if (dbri->liu_callback)
-                                dbri->liu_callback(dbri->liu_callback_arg);
-                }
+		/* UNDR - Transmission underrun
+		 * resend SDP command with clear pipe bit (C) set
+		 */
 
-                if (D_INTR_GETCODE(x) == D_INTR_BRDY) {
-                        reception_complete_intr(dbri, channel);
-                }
+		volatile int *cmd;
+		int pipe = channel;
+		int td = dbri->pipes[pipe].desc;
+
+		dbri->dma->desc[td].word4 = 0;
+
+		cmd = dbri_cmdlock(dbri);
+		*(cmd++) = DBRI_CMD(D_SDP, 0,
+				    dbri->pipes[pipe].sdp
+				    | D_SDP_P | D_SDP_C | D_SDP_2SAME);
+		*(cmd++) = (int) & dbri->dma_dvma->desc[td];
+		dbri_cmdsend(dbri, cmd);
+	}
+
+	if (code == D_INTR_FXDT) {
+
+		/* FXDT - Fixed data change */
 
-                if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
-                        transmission_complete_intr(dbri, channel);
+		if (dbri->pipes[channel].sdp & D_SDP_MSB) {
+			val = reverse_bytes(val, dbri->pipes[channel].length);
                 }
 
-                if (D_INTR_GETCODE(x) == D_INTR_FXDT) {
+		if (dbri->pipes[channel].recv_fixed_ptr) {
+			* dbri->pipes[channel].recv_fixed_ptr = val;
+		}
+	}
+}
 
-                        /* FXDT - Fixed data change */
+/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
+ * buffer until it finds a zero word (indicating nothing more to do
+ * right now).  Non-zero words require processing and are handed off
+ * to dbri_process_one_interrupt AFTER advancing the pointer.  This
+ * order is important since we might recurse back into this function
+ * and need to make sure the pointer has been advanced first.
+ */
 
-                        if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) {
-                                val = reverse_bytes(val, dbri->pipes[channel].length);
-                        }
+static void dbri_process_interrupt_buffer(struct dbri *dbri)
+{
+	int x;
 
-                        if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) {
-                                * dbri->pipes[channel].recv_fixed_ptr = val;
-                        }
-                }
+	while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
 
+		dbri->dma->intr[dbri->dbri_irqp] = 0;
 
 		dbri->dbri_irqp++;
 		if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
 			dbri->dbri_irqp = 1;
 		else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
 			dbri->dbri_irqp++;
-		x = dbri->dma->intr[dbri->dbri_irqp];
+
+		dbri_process_one_interrupt(dbri, x);
 	}
 }
 
+static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+{
+	struct dbri *dbri = (struct dbri *)opaque;
+	int x;
+	
+	/*
+	 * Read it, so the interrupt goes away.
+	 */
+	x = dbri->regs->reg1;
+
+	dprintk(D_INT, ("DBRI: Interrupt!  (reg1=0x%08x)\n", x));
+
+	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
+
+		if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
+		if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
+		if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
+		if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+
+		/* Some of these SBus errors cause the chip's SBus circuitry
+		 * to be disabled, so just re-enable and try to keep going.
+		 *
+		 * The only one I've seen is MRR, which will be triggered
+		 * if you let a transmit pipe underrun, then try to CDP it.
+		 *
+		 * If these things persist, we should probably reset
+		 * and re-init the chip.
+		 */
+
+		dbri->regs->reg0 &= ~D_D;
+	}
+
+#if 0
+	if (!(x & D_IR))	/* Not for us */
+		return;
+#endif
+
+	dbri_process_interrupt_buffer(dbri);
+}
+
 
 /*
 ****************************************************************************
 ************************** DBRI data pipe management ***********************
 ****************************************************************************
+
+While DBRI control functions use the command and interrupt buffers, the
+main data path takes the form of data pipes, which can be short (command
+and interrupt driven), or long (attached to DMA buffers).  These functions
+provide a rudimentary means of setting up and managing the DBRI's pipes,
+but the calling functions have to make sure they respect the pipes' linked
+list ordering, among other things.  The transmit and receive functions
+here interface closely with the transmit and receive interrupt code.
+
 */
 
+static int pipe_active(struct dbri *dbri, int pipe)
+{
+	return (dbri->pipes[pipe].desc != -1);
+}
+
 
 /* reset_pipe(dbri, pipe)
  *
@@ -449,6 +566,7 @@
 static void reset_pipe(struct dbri *dbri, int pipe)
 {
         int sdp;
+	int desc;
         volatile int *cmd;
 
         if (pipe < 0 || pipe > 31) {
@@ -467,6 +585,35 @@
         *(cmd++) = 0;
         dbri_cmdsend(dbri, cmd);
 
+	desc = dbri->pipes[pipe].desc;
+	while (desc != -1) {
+		void *buffer = dbri->descs[desc].buffer;
+		void (*output_callback) (void *, int)
+			= dbri->descs[desc].output_callback;
+		void *output_callback_arg
+			= dbri->descs[desc].output_callback_arg;
+		void (*input_callback) (void *, int, unsigned int)
+			= dbri->descs[desc].input_callback;
+		void *input_callback_arg
+			= dbri->descs[desc].input_callback_arg;
+
+		if (buffer) {
+			mmu_release_scsi_one(sbus_dvma_addr(buffer),
+					     dbri->descs[desc].len,
+					     dbri->sdev->my_bus);
+		}
+
+		dbri->descs[desc].inuse = 0;
+		desc = dbri->descs[desc].next;
+
+		if (output_callback) {
+			output_callback(output_callback_arg, -1);
+		}
+		if (input_callback) {
+			input_callback(input_callback_arg, -1, 0);
+		}
+	}
+
         dbri->pipes[pipe].desc = -1;
 }
 
@@ -482,140 +629,179 @@
                 /* sdp &= 0xf800; */
         }
 
+	/* If this is a fixed receive pipe, arrange for an interrupt
+	 * every time its data changes
+	 */
+
+	if (D_SDP_MODE(sdp) == D_SDP_FIXED && ! (sdp & D_SDP_TO_SER)) {
+		sdp |= D_SDP_CHANGE;
+	}
+
         sdp |= D_PIPE(pipe);
         dbri->pipes[pipe].sdp = sdp;
+	dbri->pipes[pipe].desc = -1;
 
         reset_pipe(dbri, pipe);
 }
 
-enum master_or_slave { CHImaster, CHIslave };
-
-static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
-                      int bits_per_frame)
+static void link_time_slot(struct dbri *dbri, int pipe,
+			   enum in_or_out direction, int basepipe,
+			   int length, int cycle)
 {
         volatile int *cmd;
         int val;
+	int prevpipe;
+	int nextpipe;
 
-        cmd = dbri_cmdlock(dbri);
+	if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+		printk("DBRI: link_time_slot called with illegal pipe number\n");
+		return;
+	}
 
-	/* Set CHI Anchor: Pipe 16 */
+	if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+		printk("DBRI: link_time_slot called on uninitialized pipe\n");
+		return;
+	}
 
-        val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
-                D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
-	*(cmd++) = DBRI_CMD(D_DTS, 0, val);
-	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-
-        dbri->pipes[16].sdp = 1;
-        dbri->pipes[16].nextpipe = 16;
-
-        if (master_or_slave == CHIslave) {
-                /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
-                 *
-                 * CHICM  = 0 (slave mode, 8 kHz frame rate)
-                 * IR     = give immediate CHI status interrupt
-                 * EN     = give CHI status interrupt upon change
-                 */
-                *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)
-                                    | D_CHI_IR | D_CHI_EN);
+	/* Deal with CHI special case:
+	 * "If transmission on edges 0 or 1 is desired, then cycle n
+	 *  (where n = # of bit times per frame...) must be used."
+	 *                  - DBRI data sheet, page 11
+	 */
+
+	if (basepipe == 16 && direction == PIPEoutput && cycle == 0) {
+		cycle = dbri->chi_bpf;
+	}
+
+	if (basepipe == pipe) {
+		prevpipe = pipe;
+		nextpipe = pipe;
         } else {
-                /* Setup DBRI for CHI Master - generate clock, FS
-                 *
-                 * BPF				=  bits per 8 kHz frame
-                 * 12.288 MHz / CHICM_divisor	= clock rate
-                 * FD  =  1 - drive CHIFS on rising edge of CHICK
+
+		/* We're not initializing a new linked list (basepipe != pipe),
+		 * so run through the linked list and find where this pipe
+		 * should be sloted in, based on its cycle.  CHI confuses
+		 * things a bit, since it has a single anchor for both its
+		 * transmit and receive lists.
                  */
 
-                int clockrate = bits_per_frame * 8;
-                int divisor   = 12288 / clockrate;
+		if (basepipe == 16) {
+			if (direction == PIPEinput) {
+				prevpipe = dbri->chi_in_pipe;
+			} else {
+				prevpipe = dbri->chi_out_pipe;
+			}
+		} else {
+			prevpipe = basepipe;
+		}
+
+		nextpipe = dbri->pipes[prevpipe].nextpipe;
 
-                if (divisor > 255 || divisor * clockrate != 12288) {
-                        printk("DBRI: illegal bits_per_frame in setup_chi\n");
+		while (dbri->pipes[nextpipe].cycle < cycle
+			&& dbri->pipes[nextpipe].nextpipe != basepipe) {
+			prevpipe = nextpipe;
+			nextpipe = dbri->pipes[nextpipe].nextpipe;
                 }
+	}
 
-                *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
-                                    | D_CHI_IR | D_CHI_EN
-                                    | D_CHI_BPF(bits_per_frame));
+	if (prevpipe == 16) {
+		if (direction == PIPEinput) {
+			dbri->chi_in_pipe = pipe;
+		} else {
+			dbri->chi_out_pipe = pipe;
+		}
+	} else {
+		dbri->pipes[prevpipe].nextpipe = pipe;
         }
 
-        /* CHI Data Mode
-         *
-         * RCE   =  0 - receive on falling edge of CHICK
-         * XCE   =  1 - transmit on rising edge of CHICK
-         * XEN   =  1 - enable transmitter
-         * REN   =  1 - enable receiver
-         */
+	dbri->pipes[pipe].nextpipe = nextpipe;
+	dbri->pipes[pipe].cycle = cycle;
+	dbri->pipes[pipe].length = length;
 
-        *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+	cmd = dbri_cmdlock(dbri);
 
-        *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+	if (direction == PIPEinput) {
+		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+		*(cmd++) = 0;
+	} else {
+		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = 0;
+		*(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+	}
 
         dbri_cmdsend(dbri, cmd);
 }
 
-enum in_or_out { PIPEinput, PIPEoutput };
+/* I don't use this function, so it's basically untested. */
 
-static void link_time_slot(struct dbri *dbri, int pipe,
-                           enum in_or_out direction, int prevpipe,
-                           int length, int cycle)
+static void unlink_time_slot(struct dbri *dbri, int pipe,
+			     enum in_or_out direction, int prevpipe,
+			     int nextpipe)
 {
         volatile int *cmd;
         int val;
-        int nextpipe;
 
         if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
-                printk("DBRI: link_time_slot called with illegal pipe number\n");
+		printk("DBRI: unlink_time_slot called with illegal pipe number\n");
                 return;
         }
 
-        if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) {
-                printk("DBRI: link_time_slot called on uninitialized pipe\n");
-                return;
-        }
-
-        if (pipe == prevpipe) {
-                nextpipe = pipe;
-        } else {
-                nextpipe = dbri->pipes[prevpipe].nextpipe;
-        }
-
-        dbri->pipes[pipe].nextpipe = nextpipe;
-        dbri->pipes[pipe].cycle = cycle;
-        dbri->pipes[pipe].length = length;
-
         cmd = dbri_cmdlock(dbri);
 
         if (direction == PIPEinput) {
-                val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+		val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
                 *(cmd++) = DBRI_CMD(D_DTS, 0, val);
-                *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+		*(cmd++) = D_TS_NEXT(nextpipe);
                 *(cmd++) = 0;
         } else {
-                val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+		val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe;
                 *(cmd++) = DBRI_CMD(D_DTS, 0, val);
                 *(cmd++) = 0;
-                *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+		*(cmd++) = D_TS_NEXT(nextpipe);
         }
 
         dbri_cmdsend(dbri, cmd);
 }
 
+/* xmit_fixed() / recv_fixed()
+ *
+ * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
+ * expected to change much, and which we don't need to buffer.
+ * The DBRI only interrupts us when the data changes (receive pipes),
+ * or only changes the data when this function is called (transmit pipes).
+ * Only short pipes (numbers 16-31) can be used in fixed data mode.
+ *
+ * These function operate on a 32-bit field, no matter how large
+ * the actual time slot is.  The interrupt handler takes care of bit
+ * ordering and alignment.  An 8-bit time slot will always end up
+ * in the low-order 8 bits, filled either MSB-first or LSB-first,
+ * depending on the settings passed to setup_pipe()
+ */
+
 static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data)
 {
         volatile int *cmd;
 
         if (pipe < 16 || pipe > 31) {
-                printk("DBRI: xmit_fixed called with illegal pipe number\n");
+		printk("DBRI: xmit_fixed: Illegal pipe number\n");
+		return;
+	}
+
+	if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
+		printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
                 return;
         }
 
         if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
-                printk("DBRI: xmit_fixed called on non-fixed pipe\n");
+		printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
                 return;
         }
 
         if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
-                printk("DBRI: xmit_fixed called on receive pipe\n");
+		printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
                 return;
         }
 
@@ -633,21 +819,7 @@
         dbri_cmdsend(dbri, cmd);
 }
 
-/* recv_fixed()
- *
- * Receive data on a "fixed" pipe - i.e, one whose contents are not
- * expected to change much, and which we don't need to read constantly
- * into a buffer.  The DBRI only interrupts us when the data changes.
- * Only short pipes (numbers 16-31) can be used in fixed data mode.
- *
- * Pass this function a pointer to a 32-bit field, no matter how large
- * the actual time slot is.  The interrupt handler takes care of bit
- * ordering and alignment.  An 8-bit time slot will always end up
- * in the low-order 8 bits, filled either MSB-first or LSB-first,
- * depending on the settings passed to setup_pipe()
- */
-
-static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr)
+static void recv_fixed(struct dbri *dbri, int pipe, volatile __u32 *ptr)
 {
         if (pipe < 16 || pipe > 31) {
                 printk("DBRI: recv_fixed called with illegal pipe number\n");
@@ -655,12 +827,12 @@
         }
 
         if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
-                printk("DBRI: recv_fixed called on non-fixed pipe\n");
+		printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
                 return;
         }
 
         if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
-                printk("DBRI: recv_fixed called on transmit pipe\n");
+		printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe);
                 return;
         }
 
@@ -668,37 +840,46 @@
 }
 
 
+/* xmit_on_pipe() / recv_on_pipe()
+ *
+ * Transmit/receive data on a "long" pipe - i.e, one associated
+ * with a DMA buffer.
+ *
+ * Only pipe numbers 0-15 can be used in this mode.
+ *
+ * Both functions take pointer/len arguments pointing to a data buffer,
+ * and both provide callback functions (may be NULL) to notify higher
+ * level code when transmission/reception is complete.
+ *
+ * Both work by building chains of descriptors which identify the
+ * data buffers.  Buffers too large for a single descriptor will
+ * be spread across multiple descriptors.
+ */
+
 static void xmit_on_pipe(struct dbri *dbri, int pipe,
                          void * buffer, unsigned int len,
                          void (*callback)(void *, int), void * callback_arg)
 {
         volatile int *cmd;
+	register unsigned int flags;
         int td = 0;
         int first_td = -1;
-        int last_td;
+	int last_td = -1;
         __u32 dvma_buffer;
 
         if (pipe < 0 || pipe > 15) {
-                printk("DBRI: xmit_on_pipe called with illegal pipe number\n");
+		printk("DBRI: xmit_on_pipe: Illegal pipe number\n");
                 return;
         }
 
         if (dbri->pipes[pipe].sdp == 0) {
-                printk("DBRI: xmit_on_pipe called on uninitialized pipe\n");
+		printk("DBRI: xmit_on_pipe: Uninitialized pipe %d\n", pipe);
                 return;
         }
 
         if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
-                printk("DBRI: xmit_on_pipe called on receive pipe\n");
-                return;
-        }
-
-        /* XXX Fix this XXX
-         * Should be able to queue multiple buffers to send on a pipe
-         */
-
-        if (dbri->pipes[pipe].desc != -1) {
-                printk("DBRI: xmit_on_pipe called on active pipe\n");
+		printk("DBRI: xmit_on_pipe: Called on receive pipe %d\n",
+		       pipe);
                 return;
         }
 
@@ -707,10 +888,11 @@
         while (len > 0) {
                 int mylen;
 
-                for (td; td < DBRI_NO_DESCS; td ++) {
+		for (; td < DBRI_NO_DESCS; td ++) {
                         if (! dbri->descs[td].inuse) break;
                 }
                 if (td == DBRI_NO_DESCS) {
+			printk("DBRI: xmit_on_pipe: No descriptors\n");
                         break;
                 }
 
@@ -744,15 +926,10 @@
                 len -= mylen;
         }
 
-        if (first_td == -1) {
-                printk("xmit_on_pipe: No descriptors available\n");
+	if (first_td == -1 || last_td == -1) {
                 return;
         }
 
-        if (len > 0) {
-                printk("xmit_on_pipe: Insufficient descriptors; data truncated\n");
-        }
-
         dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
 
         dbri->descs[last_td].buffer = buffer;
@@ -760,14 +937,54 @@
         dbri->descs[last_td].output_callback = callback;
         dbri->descs[last_td].output_callback_arg = callback_arg;
 
-        dbri->pipes[pipe].desc = first_td;
+	for (td=first_td; td != -1; td = dbri->descs[td].next) {
+		dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n",
+				 td,
+				 dbri->dma->desc[td].word1,
+				 dbri->dma->desc[td].ba,
+				 dbri->dma->desc[td].nda,
+				 dbri->dma->desc[td].word4));
+	}
 
-        cmd = dbri_cmdlock(dbri);
+	save_and_cli(flags);
 
-        *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
-        *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+	if (pipe_active(dbri, pipe)) {
 
-        dbri_cmdsend(dbri, cmd);
+		/* Pipe is already active - find last TD in use
+		 * and link our first TD onto its end.  Then issue
+		 * a CDP command to let the DBRI know there's more data.
+		 */
+
+		last_td = dbri->pipes[pipe].desc;
+		while (dbri->descs[last_td].next != -1)
+			last_td = dbri->descs[last_td].next;
+
+		dbri->descs[last_td].next = first_td;
+		dbri->dma->desc[last_td].nda =
+			(int) & dbri->dma_dvma->desc[first_td];
+
+		cmd = dbri_cmdlock(dbri);
+		*(cmd++) = DBRI_CMD(D_CDP, 0, pipe);
+		dbri_cmdsend(dbri,cmd);
+
+	} else {
+
+		/* Pipe isn't active - issue an SDP command to start
+		 * our chain of TDs running.
+		 */
+
+		dbri->pipes[pipe].desc = first_td;
+
+		cmd = dbri_cmdlock(dbri);
+		*(cmd++) = DBRI_CMD(D_SDP, 0,
+				    dbri->pipes[pipe].sdp
+				    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+		*(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+		dbri_cmdsend(dbri, cmd);
+
+	}
+
+	restore_flags(flags);
 }
 
 static void recv_on_pipe(struct dbri *dbri, int pipe,
@@ -776,69 +993,107 @@
                          void * callback_arg)
 {
         volatile int *cmd;
+	int first_rd = -1;
+	int last_rd = -1;
         int rd;
+	__u32 bus_buffer;
 
         if (pipe < 0 || pipe > 15) {
-                printk("DBRI: recv_on_pipe called with illegal pipe number\n");
+		printk("DBRI: recv_on_pipe: Illegal pipe number\n");
                 return;
         }
 
         if (dbri->pipes[pipe].sdp == 0) {
-                printk("DBRI: recv_on_pipe called on uninitialized pipe\n");
+		printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe);
                 return;
         }
 
         if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
-                printk("DBRI: recv_on_pipe called on transmit pipe\n");
+		printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n",
+		       pipe);
                 return;
         }
 
         /* XXX Fix this XXX
-         * Should be able to queue multiple buffers to send on a pipe
+	 * Should be able to queue multiple buffers to receive on a pipe
          */
 
         if (dbri->pipes[pipe].desc != -1) {
-                printk("DBRI: recv_on_pipe called on active pipe\n");
+		printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe);
                 return;
         }
 
-        /* XXX Fix this XXX
-         * Use multiple descriptors, if needed, to fit in all the data
-         */
-
-        if (len > (1 << 13) - 1) {
-                printk("recv_on_pipe called with len=%d; truncated\n", len);
-                len = (1 << 13) - 1;
-        }
-
         /* Make sure buffer size is multiple of four */
         len &= ~3;
 
-        for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
-                if (! dbri->descs[rd].inuse) break;
+	bus_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus);
+
+	while (len > 0) {
+		int rd;
+		int mylen;
+
+		if (len > (1 << 13) - 4) {
+			mylen = (1 << 13) - 4;
+		} else {
+			mylen = len;
+		}
+
+		for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
+			if (! dbri->descs[rd].inuse) break;
+		}
+		if (rd == DBRI_NO_DESCS) {
+			printk("DBRI recv_on_pipe: No descriptors\n");
+			break;
+		}
+
+		dbri->dma->desc[rd].word1 = 0;
+		dbri->dma->desc[rd].ba = bus_buffer;
+		dbri->dma->desc[rd].nda = 0;
+		dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen);
+
+		dbri->descs[rd].buffer = NULL;
+		dbri->descs[rd].len = 0;
+		dbri->descs[rd].input_callback = NULL;
+		dbri->descs[rd].output_callback = NULL;
+		dbri->descs[rd].next = -1;
+		dbri->descs[rd].inuse = 1;
+
+		if (first_rd == -1) first_rd = rd;
+		if (last_rd != -1) {
+			dbri->dma->desc[last_rd].nda =
+				(int) & dbri->dma_dvma->desc[rd];
+			dbri->descs[last_rd].next = rd;
+		}
+		last_rd = rd;
+
+		bus_buffer += mylen;
+		len -= mylen;
         }
-        if (rd == DBRI_NO_DESCS) {
-                printk("DBRI xmit_on_pipe: No descriptors available\n");
+
+	if (last_rd == -1 || first_rd == -1) {
                 return;
         }
 
-        dbri->dma->desc[rd].word1 = 0;
-        dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len,
-                                                  dbri->sdev->my_bus);
-        dbri->dma->desc[rd].nda = 0;
-        dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len);
-
-        dbri->descs[rd].buffer = buffer;
-        dbri->descs[rd].len = len;
-        dbri->descs[rd].input_callback = callback;
-        dbri->descs[rd].input_callback_arg = callback_arg;
+	for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) {
+		dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n",
+				 rd,
+				 dbri->dma->desc[rd].word1,
+				 dbri->dma->desc[rd].ba,
+				 dbri->dma->desc[rd].nda,
+				 dbri->dma->desc[rd].word4));
+	}
+
+	dbri->descs[last_rd].buffer = buffer;
+	dbri->descs[last_rd].len = len;
+	dbri->descs[last_rd].input_callback = callback;
+	dbri->descs[last_rd].input_callback_arg = callback_arg;
 
-        dbri->pipes[pipe].desc = rd;
+	dbri->pipes[pipe].desc = first_rd;
 
         cmd = dbri_cmdlock(dbri);
 
-        *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P);
-        *(cmd++) = (int) & dbri->dma_dvma->desc[rd];
+	*(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
+	*(cmd++) = (int) & dbri->dma_dvma->desc[first_rd];
 
         dbri_cmdsend(dbri, cmd);
 }
@@ -846,8 +1101,123 @@
 
 /*
 ****************************************************************************
+************************** DBRI - CHI interface ****************************
+****************************************************************************
+
+The CHI is a four-wire (clock, frame sync, data in, data out) time-division
+multiplexed serial interface which the DBRI can operate in either master
+(give clock/frame sync) or slave (take clock/frame sync) mode.
+
+*/
+
+enum master_or_slave { CHImaster, CHIslave };
+
+static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
+		      int bits_per_frame)
+{
+	volatile int *cmd;
+	int val;
+	static int chi_initialized=0;
+
+	if (!chi_initialized) {
+
+		cmd = dbri_cmdlock(dbri);
+
+		/* Set CHI Anchor: Pipe 16 */
+
+		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+		*(cmd++) = 0;
+
+		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = 0;
+		*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+
+		dbri->pipes[16].sdp = 1;
+		dbri->pipes[16].nextpipe = 16;
+		dbri->chi_in_pipe = 16;
+		dbri->chi_out_pipe = 16;
+
+#if 0
+		chi_initialized ++;
+#endif
+	} else {
+		int pipe;
+
+		for (pipe = dbri->chi_in_pipe;
+		     pipe != 16;
+		     pipe = dbri->pipes[pipe].nextpipe) {
+			unlink_time_slot(dbri, pipe, PIPEinput,
+					 16, dbri->pipes[pipe].nextpipe);
+		}
+		for (pipe = dbri->chi_out_pipe;
+		     pipe != 16;
+		     pipe = dbri->pipes[pipe].nextpipe) {
+			unlink_time_slot(dbri, pipe, PIPEoutput,
+					 16, dbri->pipes[pipe].nextpipe);
+		}
+
+		dbri->chi_in_pipe = 16;
+		dbri->chi_out_pipe = 16;
+
+		cmd = dbri_cmdlock(dbri);
+
+	}
+
+	if (master_or_slave == CHIslave) {
+		/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
+		 *
+		 * CHICM  = 0 (slave mode, 8 kHz frame rate)
+		 * IR     = give immediate CHI status interrupt
+		 * EN     = give CHI status interrupt upon change
+		 */
+		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+	} else {
+		/* Setup DBRI for CHI Master - generate clock, FS
+		 *
+		 * BPF				=  bits per 8 kHz frame
+		 * 12.288 MHz / CHICM_divisor	= clock rate
+		 * FD  =  1 - drive CHIFS on rising edge of CHICK
+		 */
+
+		int clockrate = bits_per_frame * 8;
+		int divisor   = 12288 / clockrate;
+
+		if (divisor > 255 || divisor * clockrate != 12288) {
+			printk("DBRI: illegal bits_per_frame in setup_chi\n");
+		}
+
+		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
+				    | D_CHI_BPF(bits_per_frame));
+	}
+
+	dbri->chi_bpf = bits_per_frame;
+
+	/* CHI Data Mode
+	 *
+	 * RCE   =  0 - receive on falling edge of CHICK
+	 * XCE   =  1 - transmit on rising edge of CHICK
+	 * XEN   =  1 - enable transmitter
+	 * REN   =  1 - enable receiver
+	 */
+
+	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+	*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+	dbri_cmdsend(dbri, cmd);
+}
+
+/*
+****************************************************************************
 *********************** CS4215 audio codec management **********************
 ****************************************************************************
+
+In the standard SPARC audio configuration, the CS4215 codec is attached
+to the DBRI via the CHI interface and few of the DBRI's PIO pins.
+
 */
 
 
@@ -872,21 +1242,83 @@
 	 * 2: Serial enable, CHI master, 128 bits per frame, clock 1
 	 * 3: Tests disabled
 	 */
-	mm->ctrl[0] = CS4215_RSRVD_1;
+	mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
 	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
 	mm->ctrl[2] = CS4215_XCLK |
 			CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
 	mm->ctrl[3] = 0;
 }
 
+static void mmcodec_setup_pipes(struct dbri *dbri)
+{
+	/*
+	 * Data mode:
+	 * Pipe  4: Send timeslots 1-4 (audio data)
+	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
+	 * Pipe  6: Receive timeslots 1-4 (audio data)
+	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
+	 *          interrupt, and the rest of the data (slot 5 and 8) is
+	 *	    not relevant for us (only for doublechecking).
+	 *
+	 * Control mode:
+	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+	 * Pipe 18: Receive timeslot 1 (clb).
+	 * Pipe 19: Receive timeslot 7 (version). 
+	 */
+
+	setup_pipe(dbri,  4, D_SDP_MEM   | D_SDP_TO_SER | D_SDP_MSB);
+	setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+	setup_pipe(dbri,  6, D_SDP_MEM   | D_SDP_FROM_SER | D_SDP_MSB);
+	setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+	setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER   | D_SDP_MSB);
+	setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+	setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+	dbri->mm.status = 0;
+
+	recv_fixed(dbri, 18, & dbri->mm.status);
+	recv_fixed(dbri, 19, & dbri->mm.version);
+}
+
+static void mmcodec_setgain(struct dbri *dbri, int muted)
+{
+	if (muted || dbri->perchip_info.output_muted) {
+		dbri->mm.data[0] = 63;
+		dbri->mm.data[1] = 63;
+	} else {
+		int left_gain = (dbri->perchip_info.play.gain / 4) % 64;
+		int right_gain = (dbri->perchip_info.play.gain / 4) % 64;
+
+		if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) {
+			right_gain *= dbri->perchip_info.play.balance;
+			right_gain /= AUDIO_MID_BALANCE;
+		} else {
+			left_gain *= AUDIO_RIGHT_BALANCE
+				- dbri->perchip_info.play.balance;
+			left_gain /= AUDIO_MID_BALANCE;
+		}
+
+		dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n",
+			       left_gain, right_gain));
+
+		dbri->mm.data[0] = CS4215_LE | CS4215_HE | (63 - left_gain);
+		dbri->mm.data[1] = CS4215_SE | (63 - right_gain);
+	}
+
+	xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
+}
+
 static void mmcodec_init_data(struct dbri *dbri)
 {
+	int data_width;
+
 	/*
 	 * Data mode:
 	 * Pipe  4: Send timeslots 1-4 (audio data)
-	 * Pipe 17: Send timeslots 5-8 (part of ctrl data)
+	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
 	 * Pipe  6: Receive timeslots 1-4 (audio data)
-	 * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via
+	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
 	 *          interrupt, and the rest of the data (slot 5 and 8) is
 	 *	    not relevant for us (only for doublechecking).
          *
@@ -896,35 +1328,55 @@
 	 */
 
 
+	dbri->regs->reg0 &= ~D_C;	/* Disable CHI */
+
         /* Switch CS4215 to data mode - set PIO3 to 1 */
 	dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
 				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
 
-	reset_chi(dbri, CHIslave, 0);
+	reset_chi(dbri, CHIslave, 128);
+
+	/* Note: this next doesn't work for 8-bit stereo, because the two
+	 * channels would be on timeslots 1 and 3, with 2 and 4 idle.
+	 * (See CS4215 datasheet Fig 15)
+	 *
+	 * DBRI non-contiguous mode would be required to make this work.
+	 */
 
-        setup_pipe(dbri,  4, D_SDP_MEM   | D_SDP_TO_SER | D_SDP_MSB);
-        setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
-        setup_pipe(dbri,  6, D_SDP_MEM   | D_SDP_FROM_SER | D_SDP_MSB);
-        setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+	data_width = dbri->perchip_info.play.channels
+		* dbri->perchip_info.play.precision;
 
-        /* Pipes 4 and 6 - Single time slot, 8 bit mono */
+	link_time_slot(dbri, 20, PIPEoutput, 16,
+		       32, dbri->mm.offset + 32);
+	link_time_slot(dbri,  4, PIPEoutput, 16,
+		       data_width, dbri->mm.offset);
+	link_time_slot(dbri,  6, PIPEinput, 16,
+		       data_width, dbri->mm.offset);
+	link_time_slot(dbri, 21, PIPEinput, 16,
+		       16, dbri->mm.offset + 40);
 
-        link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32);
-        link_time_slot(dbri,  4, PIPEoutput, 17, 8, 128);
-        link_time_slot(dbri,  6, PIPEinput, 16, 8, 0);
-        link_time_slot(dbri, 20, PIPEinput, 6, 16, 40);
+	mmcodec_setgain(dbri, 0);
 
-        xmit_fixed(dbri, 17, *(int *)dbri->mm.data);
+	dbri->regs->reg0 |= D_C;	/* Enable CHI */
 }
 
 
 /*
  * Send the control information (i.e. audio format)
  */
-static void mmcodec_setctrl(struct dbri *dbri)
+static int mmcodec_setctrl(struct dbri *dbri)
 {
 	int i, val;
 
+	/* XXX - let the CPU do something useful during these delays */
+
+	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
+	 * to make sure this takes.  This avoids clicking noises.
+	 */
+
+	mmcodec_setgain(dbri, 1);
+	udelay(125);
+
 	/*
 	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
 	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
@@ -952,6 +1404,8 @@
          * frame sync signal by eight clock cycles.  Anybody know why?
          */
 
+	dbri->regs->reg0 &= ~D_C;	/* Disable CHI */
+
         reset_chi(dbri, CHImaster, 128);
 
 	/*
@@ -961,27 +1415,26 @@
 	 * Pipe 19: Receive timeslot 7 (version). 
 	 */
 
-        setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
-        setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
-        setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
-
-        link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128);
-        link_time_slot(dbri, 18, PIPEinput, 16, 8, 0);
-        link_time_slot(dbri, 19, PIPEinput, 18, 8, 48);
-
-        recv_fixed(dbri, 18, & dbri->mm.status);
-        recv_fixed(dbri, 19, & dbri->mm.version);
+	link_time_slot(dbri, 17, PIPEoutput, 16,
+		       32, dbri->mm.offset);
+	link_time_slot(dbri, 18, PIPEinput, 16,
+		       8, dbri->mm.offset);
+	link_time_slot(dbri, 19, PIPEinput, 16,
+		       8, dbri->mm.offset + 48);
 
         /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
 
 	dbri->mm.ctrl[0] &= ~CS4215_CLB;
         xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
 
-        i = 1000000;
-        while ((! dbri->mm.status & CS4215_CLB) && i--);
+	dbri->regs->reg0 |= D_C;	/* Enable CHI */
+
+	i = 10;
+	while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125);
         if (i == 0) {
-                printk("CS4215 didn't respond to CLB\n");
-		return;
+		dprintk(D_MM, ("DBRI: CS4215 didn't respond to CLB (0x%02x)\n",
+			       dbri->mm.status));
+		return -1;
         }
 
         /* Terminate CS4215 control mode - data sheet says
@@ -993,6 +1446,10 @@
 
         /* Two frames of control info @ 8kHz frame rate = 250 us delay */
         udelay(250);
+
+	mmcodec_setgain(dbri, 0);
+
+	return 0;
 }
 
 static int mmcodec_init(struct sparcaudio_driver *drv)
@@ -1024,16 +1481,24 @@
 	}
 
 
-	/* Now talk to our baby */
-	dbri->regs->reg0 |= D_C;	/* Enable CHI */
+	mmcodec_setup_pipes(dbri);
 
 	mmcodec_default(&dbri->mm);
 
 	dbri->mm.version = 0xff;
-	mmcodec_setctrl(dbri);
-	if(dbri->mm.version == 0xff) 
+	dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
+	if (mmcodec_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
+		dprintk(D_MM, ("DBRI: CS4215 failed probe at offset %d\n",
+			       dbri->mm.offset));
 		return -EIO;
+	}
+
+	dprintk(D_MM, ("DBRI: Found CS4215 at offset %d\n", dbri->mm.offset));
 
+	dbri->perchip_info.play.channels = 1;
+	dbri->perchip_info.play.precision = 8;
+	dbri->perchip_info.play.gain = 255;
+	dbri->perchip_info.play.balance = AUDIO_MID_BALANCE;
 	mmcodec_init_data(dbri);
 
 	return 0;
@@ -1044,37 +1509,25 @@
 ****************************************************************************
 ******************** Interface with sparcaudio midlevel ********************
 ****************************************************************************
-*/
 
+The sparcaudio midlevel is contained in the file audio.c.  It interfaces
+to the user process and performs buffering, intercepts SunOS-style ioctl's,
+etc.  It interfaces to a abstract audio device via a struct sparcaudio_driver.
+This code presents such an interface for the DBRI with an attached CS4215.
+All our routines are defined, and then comes our struct sparcaudio_driver.
 
-static int dbri_open(struct inode * inode, struct file * file,
-                     struct sparcaudio_driver *drv)
-{
-	struct dbri *dbri = (struct dbri *)drv->private;
-
-	MOD_INC_USE_COUNT;
+*/
 
-	return 0;
-}
+/******************* sparcaudio midlevel - audio output *******************/
 
-static void dbri_release(struct inode * inode, struct file * file,
-                         struct sparcaudio_driver *drv)
-{
-	MOD_DEC_USE_COUNT;
-}
-
-static int dbri_ioctl(struct inode * inode, struct file * file,
-                      unsigned int x, unsigned long y,
-                      struct sparcaudio_driver *drv)
-{
-        return 0;
-}
 
 static void dbri_audio_output_callback(void * callback_arg, int status)
 {
         struct sparcaudio_driver *drv = callback_arg;
 
-        sparcaudio_output_done(drv, 1);
+	if (status != -1) {
+		sparcaudio_output_done(drv, 1);
+	}
 }
 
 static void dbri_start_output(struct sparcaudio_driver *drv,
@@ -1082,8 +1535,31 @@
 {
 	struct dbri *dbri = (struct dbri *)drv->private;
 
+	dprintk(D_USR, ("DBRI: start audio output buf=%lx/%ld\n",
+			(unsigned long) buffer, count));
+
         /* Pipe 4 is audio transmit */
-        xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv);
+
+	xmit_on_pipe(dbri, 4, buffer, count,
+		     &dbri_audio_output_callback, drv);
+
+#if 0
+	/* Notify midlevel that we're a DMA-capable driver that
+	 * can accept another buffer immediately.  We should probably
+	 * check that we've got enough resources (i.e, descriptors)
+	 * available before doing this, but the default midlevel
+	 * settings only buffer 64KB, which we can handle with 16
+	 * of our DBRI_NO_DESCS (64) descriptors.
+	 *
+	 * This code is #ifdef'ed out because it's caused me more
+	 * problems than it solved.  It'd be nice to provide the
+	 * DBRI with a chain of buffers, but the midlevel code is
+	 * so tricky that I really don't want to deal with it.
+	 */
+
+	sparcaudio_output_done(drv, 2);
+#endif
+
 }
 
 static void dbri_stop_output(struct sparcaudio_driver *drv)
@@ -1093,28 +1569,55 @@
         reset_pipe(dbri, 4);
 }
 
+/******************* sparcaudio midlevel - audio input ********************/
+
+static void dbri_audio_input_callback(void * callback_arg, int status,
+				      unsigned int len)
+{
+	struct sparcaudio_driver * drv =
+		(struct sparcaudio_driver *) callback_arg;
+
+	if (status != -1) {
+		sparcaudio_input_done(drv, 3);
+	}
+}
+
 static void dbri_start_input(struct sparcaudio_driver *drv,
                              __u8 * buffer, unsigned long len)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	/* Pipe 6 is audio receive */
+	recv_on_pipe(dbri, 6, buffer, len,
+		     &dbri_audio_input_callback, (void *)drv);
+	dprintk(D_USR, ("DBRI: start audio input buf=%lx/%ld\n",
+			(unsigned long) buffer, len));
 }
 
 static void dbri_stop_input(struct sparcaudio_driver *drv)
 {
-}
+	struct dbri *dbri = (struct dbri *)drv->private;
 
-static void dbri_audio_getdev(struct sparcaudio_driver *drv,
-                              audio_device_t *devptr)
-{
+	reset_pipe(dbri, 6);
 }
 
+/******************* sparcaudio midlevel - volume & balance ***************/
+
 static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	dbri->perchip_info.play.gain = volume;
+	mmcodec_setgain(dbri, 0);
+
         return 0;
 }
 
 static int dbri_get_output_volume(struct sparcaudio_driver *drv)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.gain;
 }
 
 static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume)
@@ -1139,12 +1642,19 @@
 
 static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	dbri->perchip_info.play.balance = balance;
+	mmcodec_setgain(dbri, 0);
+
         return 0;
 }
 
 static int dbri_get_output_balance(struct sparcaudio_driver *drv)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.balance;
 }
 
 static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance)
@@ -1157,107 +1667,205 @@
         return 0;
 }
 
+static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	dbri->perchip_info.output_muted = mute;
+
+	return 0;
+}
+
+static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.output_muted;
+}
+
+/******************* sparcaudio midlevel - encoding format ****************/
+
 static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	switch (chan) {
+	case 0:
+		return 0;
+	case 1:
+		dbri->mm.ctrl[1] &= ~CS4215_DFR_STEREO;
+		break;
+	case 2:
+		dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
+		break;
+	default:
+		return -1;
+	}
+
+	dbri->perchip_info.play.channels = chan;
+	mmcodec_setctrl(dbri);
+	mmcodec_init_data(dbri);
         return 0;
 }
 
 static int dbri_get_output_channels(struct sparcaudio_driver *drv)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.channels;
 }
 
 static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan)
 {
-        return 0;
+	return dbri_set_output_channels(drv, chan);
 }
 
 static int dbri_get_input_channels(struct sparcaudio_driver *drv)
 {
-        return 0;
+	return dbri_get_output_channels(drv);
 }
 
 static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec)
 {
-        return 8;
+	return 0;
 }
 
 static int dbri_get_output_precision(struct sparcaudio_driver *drv)
 {
-        return 8;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.precision;
 }
 
 static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec)
 {
-        return 8;
+	return 0;
 }
 
 static int dbri_get_input_precision(struct sparcaudio_driver *drv)
 {
-        return 8;
-}
+	struct dbri *dbri = (struct dbri *)drv->private;
 
-static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
-{
-        return 0;
+	return dbri->perchip_info.play.precision;
 }
 
-static int dbri_get_output_port(struct sparcaudio_driver *drv)
+static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	/* For ULAW and ALAW, audio.c enforces precision = 8,
+	 * for LINEAR, precision must be 16
+	 */
+
+	switch (enc) {
+	case AUDIO_ENCODING_NONE:
+		return 0;
+	case AUDIO_ENCODING_ULAW:
+		dbri->mm.ctrl[1] &= ~3;
+		dbri->mm.ctrl[1] |= CS4215_DFR_ULAW;
+		dbri->perchip_info.play.encoding = enc;
+		dbri->perchip_info.play.precision = 8;
+		break;
+	case AUDIO_ENCODING_ALAW:
+		dbri->mm.ctrl[1] &= ~3;
+		dbri->mm.ctrl[1] |= CS4215_DFR_ALAW;
+		dbri->perchip_info.play.encoding = enc;
+		dbri->perchip_info.play.precision = 8;
+		break;
+	case AUDIO_ENCODING_LINEAR:
+		dbri->mm.ctrl[1] &= ~3;
+		dbri->mm.ctrl[1] |= CS4215_DFR_LINEAR16;
+		dbri->perchip_info.play.encoding = enc;
+		dbri->perchip_info.play.precision = 16;
+		break;
+	default:
+		return -1;
+	}
+	mmcodec_setctrl(dbri);
+	mmcodec_init_data(dbri);
         return 0;
 }
 
-static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
+static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.encoding;
 }
 
-static int dbri_get_input_port(struct sparcaudio_driver *drv)
+static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
 {
-        return 0;
+	return dbri_set_output_encoding(drv, enc);
 }
 
-static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
 {
-        return 0;
+	return dbri_get_output_encoding(drv);
 }
 
-static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
 {
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int i;
+
+	if (rate == 0) {
+		return 0;
+	}
+
+	for (i=0; CS4215_FREQ[i].freq; i++) {
+		if (CS4215_FREQ[i].freq == rate) break;
+	}
+	if (CS4215_FREQ[i].freq == 0) {
+		return -1;
+	}
+
+	dbri->mm.ctrl[1] &= ~ 0x38;
+	dbri->mm.ctrl[1] |= CS4215_FREQ[i].csval;
+	dbri->mm.ctrl[2] &= ~ 0x70;
+	dbri->mm.ctrl[2] |= CS4215_FREQ[i].xtal;
+
+	dbri->perchip_info.play.sample_rate = rate;
+
+	mmcodec_setctrl(dbri);
+	mmcodec_init_data(dbri);
         return 0;
 }
 
-static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_output_rate(struct sparcaudio_driver *drv)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	return dbri->perchip_info.play.sample_rate;
 }
 
-static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
 {
-        return 0;
+	return dbri_set_output_rate(drv, rate);
 }
 
-static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_input_rate(struct sparcaudio_driver *drv)
 {
-        return 0;
+	return dbri_get_output_rate(drv);
 }
 
-static int dbri_get_output_rate(struct sparcaudio_driver *drv)
+/******************* sparcaudio midlevel - ports ***********************/
+
+static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
 {
         return 0;
 }
 
-static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_output_port(struct sparcaudio_driver *drv)
 {
         return 0;
 }
 
-static int dbri_get_input_rate(struct sparcaudio_driver *drv)
+static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
 {
         return 0;
 }
 
-static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+static int dbri_get_input_port(struct sparcaudio_driver *drv)
 {
         return 0;
 }
@@ -1272,17 +1880,66 @@
         return 0;
 }
 
-static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+/******************* sparcaudio midlevel - driver ID ********************/
+
+static void dbri_audio_getdev(struct sparcaudio_driver *drv,
+			      audio_device_t *audinfo)
 {
-        return 0;
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+	strncpy(audinfo->name, "SUNW,DBRI", sizeof(audinfo->name) - 1);
+
+	audinfo->version[0] = dbri->dbri_version;
+	audinfo->version[1] = '\0';
+
+	strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1);
 }
 
-static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
 {
-        return 0;
+	return AUDIO_DEV_CODEC;
 }
 
+/******************* sparcaudio midlevel - open & close ******************/
 
+static int dbri_open(struct inode * inode, struct file * file,
+		     struct sparcaudio_driver *drv)
+{
+	MOD_INC_USE_COUNT;
+
+	/* SunOS 5.5.1 audio(7I) man page says:
+	 * "Upon the initial open() of the audio device, the driver
+	 *  will reset the data format of the device to the default
+	 *  state of 8-bit, 8KHz, mono u-law data."
+	 *
+	 * I've also taken the liberty of setting half gain and
+	 * mid balance, to put the codec in a known state.
+	 */
+
+	dbri_set_output_channels(drv, 1);
+	dbri_set_output_encoding(drv, AUDIO_ENCODING_ULAW);
+	dbri_set_output_rate(drv, 8000);
+
+	dbri_set_output_balance(drv, AUDIO_MID_BALANCE);
+	dbri_set_output_volume(drv, AUDIO_MAX_GAIN/2);
+
+	return 0;
+}
+
+static void dbri_release(struct inode * inode, struct file * file,
+			 struct sparcaudio_driver *drv)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static int dbri_ioctl(struct inode * inode, struct file * file,
+		      unsigned int x, unsigned long y,
+		      struct sparcaudio_driver *drv)
+{
+	return -EINVAL;
+}
+
+/*********** sparcaudio midlevel - struct sparcaudio_driver ************/
 
 static struct sparcaudio_operations dbri_ops = {
 	dbri_open,
@@ -1357,8 +2014,8 @@
         setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
 
         link_time_slot(dbri, 0, PIPEinput, 0, 2, 17);
-        link_time_slot(dbri, 8, PIPEinput, 8, 8, 0);
-        link_time_slot(dbri, 9, PIPEinput, 9, 8, 8);
+	link_time_slot(dbri, 8, PIPEinput, 0, 8, 0);
+	link_time_slot(dbri, 9, PIPEinput, 8, 8, 8);
 
         link_time_slot(dbri,  1, PIPEoutput,  1, 2, 17);
         link_time_slot(dbri, 10, PIPEoutput,  1, 8, 0);
@@ -1375,6 +2032,8 @@
 
        dbri = (struct dbri *) drivers[dev].private;
 
+       tprintk(("dbri_get_irqnum()\n"));
+
         /* On the sparc, the cpu's irq number is only part of the "irq" */
        return (dbri->irq & NR_IRQS);
 }
@@ -1389,6 +2048,8 @@
 
        dbri = (struct dbri *) drivers[dev].private;
 
+       tprintk(("dbri_get_liu_state() returns %d\n", dbri->liu_state));
+
        return dbri->liu_state;
 }
 
@@ -1404,12 +2065,14 @@
 
        dbri = (struct dbri *) drivers[dev].private;
 
+       tprintk(("dbri_liu_init()\n"));
+
        /* Set callback for LIU state change */
-        dbri->liu_callback = callback;
+       dbri->liu_callback = callback;
        dbri->liu_callback_arg = callback_arg;
 
-        dbri_isdn_init(dbri);
-        dbri_liu_activate(dev, 0);
+       dbri_isdn_init(dbri);
+       dbri_liu_activate(dev, 0);
 }
 
 void dbri_liu_activate(int dev, int priority)
@@ -1424,16 +2087,24 @@
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        cmd = dbri_cmdlock(dbri);
+       tprintk(("dbri_liu_activate()\n"));
 
-        /* Turn on the ISDN TE interface and request activation */
-        val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
-        *(cmd++) = DBRI_CMD(D_TE, 0, val);
+       if (dbri->liu_state <= 3) {
 
-       dbri_cmdsend(dbri, cmd);
+	       cmd = dbri_cmdlock(dbri);
+
+	       /* Turn on the ISDN TE interface and request activation */
+	       val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
+#ifdef LOOPBACK_D
+	       val |= D_NT_LLB(4);
+#endif
+	       *(cmd++) = DBRI_CMD(D_TE, 0, val);
 
-        /* Activate the interface */
-        dbri->regs->reg0 |= D_T;
+	       dbri_cmdsend(dbri, cmd);
+
+	       /* Activate the interface */
+	       dbri->regs->reg0 |= D_T;
+       }
 }
 
 void dbri_liu_deactivate(int dev)
@@ -1446,8 +2117,14 @@
 
        dbri = (struct dbri *) drivers[dev].private;
 
+       tprintk(("dbri_liu_deactivate()\n"));
+
+#if 0
         /* Turn off the ISDN TE interface */
         dbri->regs->reg0 &= ~D_T;
+
+	dbri->liu_state = 0;
+#endif
 }
 
 void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
@@ -1613,6 +2290,11 @@
                                        "DBRI DMA Cmd Block", &dma_dvma);
         dbri->dma_dvma = (struct dbri_dma *) dma_dvma;
 
+	memset((void *) dbri->dma, 0, sizeof(struct dbri_dma));
+
+	dprintk(D_GEN, ("DBRI: DMA Cmd Block 0x%08x (0x%08x)\n",
+			(int)dbri->dma, (int)dbri->dma_dvma));
+
 	dbri->dbri_version = sdev->prom_name[9];
         dbri->sdev = sdev;
 
@@ -1625,6 +2307,8 @@
 		"DBRI Registers", sdev->reg_addrs[0].which_io, 0);
 	if (!dbri->regs) {
 		printk(KERN_ERR "DBRI: could not allocate registers\n");
+		release_region((unsigned long) dbri->dma,
+			       sizeof(struct dbri_dma));
 		kfree(drv->private);
 		return -EIO;
 	}
@@ -1637,6 +2321,8 @@
 	if (err) {
 		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
 		sparc_free_io(dbri->regs, dbri->regs_size);
+		release_region((unsigned long) dbri->dma,
+			       sizeof(struct dbri_dma));
 		kfree(drv->private);
 		return err;
 	}
diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/dbri.h linux/drivers/sbus/audio/dbri.h
--- v2.3.6/linux/drivers/sbus/audio/dbri.h	Wed May 12 08:41:15 1999
+++ linux/drivers/sbus/audio/dbri.h	Thu Jun 17 01:08:50 1999
@@ -46,13 +46,19 @@
   struct dbri_mem desc[DBRI_NO_DESCS];		/* Xmit/receive descriptors */
 };
 
+enum in_or_out { PIPEinput, PIPEoutput };
+
+enum direction { in, out };
+
 struct dbri_pipe {
         u32 sdp;				/* SDP command word */
+	enum direction direction;
         int nextpipe;				/* Next pipe in linked list */
+	int prevpipe;
         int cycle;				/* Offset of timeslot (bits) */
         int length;				/* Length of timeslot (bits) */
         int desc;				/* Index of active descriptor*/
-        __u32 *recv_fixed_ptr;			/* Ptr to receive fixed data */
+	volatile __u32 *recv_fixed_ptr;		/* Ptr to receive fixed data */
 };
 
 struct dbri_desc {
@@ -78,10 +84,15 @@
   struct dbri_regs *regs;			/* dbri HW regs */
   int dbri_version;				/* 'e' and up is OK */
   int dbri_irqp;				/* intr queue pointer */
+  int wait_seen;
 
   struct dbri_pipe pipes[32];			/* DBRI's 32 data pipes */
   struct dbri_desc descs[DBRI_NO_DESCS];
 
+  int chi_in_pipe;
+  int chi_out_pipe;
+  int chi_bpf;
+
   struct cs4215 mm;				/* mmcodec special info */
 
 #if 0
@@ -190,7 +201,7 @@
 /* Time Slot defines */
 #define D_TS_LEN(v)	((v)<<24)	/* Number of bits in this time slot */
 #define D_TS_CYCLE(v)	((v)<<14)	/* Bit Count at start of TS */
-#define D_TS_DI(v)	(1<<13)	/* Data Invert */
+#define D_TS_DI		(1<<13)	/* Data Invert */
 #define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
 #define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
 #define D_TS_NONCONTIG	(3<<10) /* Non contiguous mode */
@@ -218,8 +229,8 @@
 #define D_NT_IFA	(1<<10)	/* Inhibit Final Activation */
 #define D_NT_ACT	(1<<9)	/* Activate Interface */
 #define D_NT_MFE	(1<<8)	/* Multiframe Enable */
-#define D_NT_RLB(v)	(1<<5)	/* Remote Loopback */
-#define D_NT_LLB(v)	(1<<2)	/* Local Loopback */
+#define D_NT_RLB(v)	((v)<<5)	/* Remote Loopback */
+#define D_NT_LLB(v)	((v)<<2)	/* Local Loopback */
 #define D_NT_FACT	(1<<1)	/* Force Activation */
 #define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
 
diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.3.6/linux/drivers/sbus/char/su.c	Wed Jun  9 14:44:25 1999
+++ linux/drivers/sbus/char/su.c	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $
+/* $Id: su.c,v 1.21 1999/06/11 10:23:42 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -1133,9 +1133,9 @@
 	struct su_struct *info = su_table;
 	int lsr;
 
-	if (!info->port_type != SU_PORT_KBD)
+	if (info->port_type != SU_PORT_KBD)
 		++info;
-	if (!info->port_type != SU_PORT_KBD)
+	if (info->port_type != SU_PORT_KBD)
 		return;
 
 	do {
@@ -1151,9 +1151,9 @@
 {
 	struct su_struct *info = su_table;
 
-	if (!info->port_type != SU_PORT_MS)
+	if (info->port_type != SU_PORT_MS)
 		++info;
-	if (!info->port_type != SU_PORT_MS)
+	if (info->port_type != SU_PORT_MS)
 		return;
 
 	info->cflag &= ~(CBAUDEX | CBAUD);
@@ -2215,7 +2215,7 @@
  */
 __initfunc(static __inline__ void show_su_version(void))
 {
-	char *revision = "$Revision: 1.20 $";
+	char *revision = "$Revision: 1.21 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
diff -u --recursive --new-file v2.3.6/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v2.3.6/linux/drivers/scsi/in2000.h	Tue Jun  8 23:04:31 1999
+++ linux/drivers/scsi/in2000.h	Sun Jun 20 17:47:14 1999
@@ -67,7 +67,7 @@
    orl %%ecx, %%ecx       \n \
    jz 1f                  \n \
    rep                    \n \
-   insw %%dx              \n \
+   insw (%%dx),%%es:(%%edi) \n \
 1: "                       \
    : "=D" (sp)                   /* output */   \
    : "d" (f), "D" (sp), "c" (i)  /* input */    \
@@ -79,7 +79,7 @@
    orl %%ecx, %%ecx       \n \
    jz 1f                  \n \
    rep                    \n \
-   outsw %%dx             \n \
+   outsw %%ds:(%%esi),(%%dx) \n \
 1: "                       \
    : "=S" (sp)                   /* output */   \
    : "d" (f), "S" (sp), "c" (i)  /* input */    \
diff -u --recursive --new-file v2.3.6/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c
--- v2.3.6/linux/drivers/scsi/scsi_error.c	Tue May 11 14:37:40 1999
+++ linux/drivers/scsi/scsi_error.c	Wed Jun 16 19:26:27 1999
@@ -1926,6 +1926,7 @@
 	int	               rtn;
         DECLARE_MUTEX_LOCKED(sem);
         unsigned long flags;
+        struct fs_struct *fs;
 
 	lock_kernel();
 
@@ -1936,16 +1937,18 @@
 	 */
 	exit_mm(current);
 
-
 	current->session = 1;
 	current->pgrp = 1;
-        /*
-         * FIXME(eric) this is still a child process of the one that did the insmod.
-         * This needs to be attached to task[0] instead.
-         */
+	
+	/* Become as one with the init task */
+	
+	exit_fs(current);	/* current->fs->count--; */
+	fs = init_task.fs;
+	current->fs = fs;
+	atomic_inc(&fs->count);
 
 	siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
-        current->fs->umask = 0;
+
 
 	/*
 	 * Set the name of this process.
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.3.6/linux/drivers/sound/es1370.c	Sat May 22 13:05:35 1999
+++ linux/drivers/sound/es1370.c	Fri Jun 18 10:45:58 1999
@@ -98,6 +98,8 @@
  *                     (micz). From Kim.Berts@fisub.mail.abb.com
  *    11.05.99   0.22  Implemented the IMIX call to mute recording monitor.
  *                     Guenter Geiger <geiger@epy.co.at>
+ *    15.06.99   0.23  Fix bad allocation bug.
+ *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -531,8 +533,9 @@
 	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
 	if (!db->rawbuf) {
 		db->ready = db->mapped = 0;
-		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
-			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order);
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order)))
+				break;
 		if (!db->rawbuf)
 			return -ENOMEM;
 		db->buforder = order;
@@ -2317,7 +2320,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.23 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.3.6/linux/drivers/sound/es1371.c	Wed May 12 13:27:37 1999
+++ linux/drivers/sound/es1371.c	Fri Jun 18 10:45:58 1999
@@ -65,6 +65,8 @@
  *                     reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru>
  *                     Note: joystick address handling might still be wrong on archs
  *                     other than i386
+ *    15.06.99   0.12  Fix bad allocation bug.
+ *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *
  */
 
@@ -759,8 +761,9 @@
 	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
 	if (!db->rawbuf) {
 		db->ready = db->mapped = 0;
-		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
-			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order);
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order)))
+				break;
 		if (!db->rawbuf)
 			return -ENOMEM;
 		db->buforder = order;
@@ -2732,7 +2735,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.11 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1371: version v0.12 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.3.6/linux/drivers/sound/sonicvibes.c	Wed May 12 13:27:37 1999
+++ linux/drivers/sound/sonicvibes.c	Fri Jun 18 10:45:58 1999
@@ -68,6 +68,8 @@
  *                     SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
  *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
  *                     Note: dmaio hack might still be wrong on archs other than i386
+ *    15.06.99   0.15  Fix bad allocation bug.
+ *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *
  */
 
@@ -699,8 +701,9 @@
 	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
 	if (!db->rawbuf) {
 		db->ready = db->mapped = 0;
-		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
-			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
+				break;
 		if (!db->rawbuf)
 			return -ENOMEM;
 		db->buforder = order;
@@ -2321,7 +2324,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.14 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.15 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc.c linux/drivers/sound/vidc.c
--- v2.3.6/linux/drivers/sound/vidc.c	Wed Dec 16 12:52:01 1998
+++ linux/drivers/sound/vidc.c	Thu Jun 17 01:11:35 1999
@@ -24,7 +24,7 @@
 #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
 
 	fillertype = TYPE(format, channels);
-printk("filler type: %X\n", fillertype);
+
 	switch (fillertype)
 	{
 		default:
@@ -61,10 +61,13 @@
 
 	sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
 	conf_printf(name, hw_config);
+	memset(dma_buf, 0, sizeof(dma_buf));
 
 	for (i = 0; i < 2; i++)
 	{
 		dma_buf[i] = get_free_page(GFP_KERNEL);
+		if (!dma_buf[i])
+			goto nomem;
 		dma_pbuf[i] = virt_to_phys(dma_buf[i]);
 	}
 
@@ -78,9 +81,16 @@
 		printk(KERN_ERR "VIDCsound: can't allocate DMA interrupt\n");
 		return;
 	}
+
 //	vidc_synth_init(hw_config);
 	vidc_audio_init(hw_config);
 	vidc_mixer_init(hw_config);
+	return;
+
+nomem:
+	for (i = 0; i < 2; i++)
+		free_page(dma_buf[i]);
+	printk(KERN_ERR "VIDCsound: can't allocate required buffers\n");
 }
 
 int probe_vidc(struct address_info *hw_config)
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc_audio.c linux/drivers/sound/vidc_audio.c
--- v2.3.6/linux/drivers/sound/vidc_audio.c	Wed Dec 16 12:52:01 1998
+++ linux/drivers/sound/vidc_audio.c	Thu Jun 17 01:11:35 1999
@@ -9,6 +9,7 @@
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/iomd.h>
 
 #include "sound_config.h"
 #include "vidc.h"
@@ -42,7 +43,6 @@
 
 static int vidc_audio_set_bits(int fmt)
 {
-printk("setting format: %d\n", fmt);
 	switch (fmt)
 	{
 		case AFMT_QUERY:
@@ -219,10 +219,11 @@
 	if (!(adev->flags & DMA_ACTIVE))
 	{
 		unsigned long flags;
-printk("kicking output: %lX+%lX [%lX]\n", dma_start, dma_count, *(unsigned long *)dma_start);
 		save_flags_cli(flags);
+
 		vidc_sound_dma_irq(0, NULL, NULL);
 		outb(DMA_CR_E | 0x10, IOMD_SD0CR);
+
 		restore_flags(flags);
 	}
 }
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc_fill.S linux/drivers/sound/vidc_fill.S
--- v2.3.6/linux/drivers/sound/vidc_fill.S	Wed Dec 16 12:52:01 1998
+++ linux/drivers/sound/vidc_fill.S	Thu Jun 17 01:11:35 1999
@@ -9,6 +9,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/hardware.h>
+#include <asm/iomd.h>
 
 		.text
 
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/waveartist.c linux/drivers/sound/waveartist.c
--- v2.3.6/linux/drivers/sound/waveartist.c	Thu Jan  7 09:24:00 1999
+++ linux/drivers/sound/waveartist.c	Thu Jun 17 01:11:35 1999
@@ -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,269 @@
 
 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);
+
+/*
+ * Corel Netwinder specifics...
+ */
 static struct timer_list vnc_timer;
+extern spinlock_t gpio_lock;
+
+static void
+vnc_mute(wavnc_info *devc, int mute)
+{
+	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;
+
+		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;
+	}
+#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)
@@ -1368,138 +1637,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,139 +1803,6 @@
 	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)
 {
@@ -1808,12 +1818,12 @@
 		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;
 	}
diff -u --recursive --new-file v2.3.6/linux/drivers/sound/waveartist.h linux/drivers/sound/waveartist.h
--- v2.3.6/linux/drivers/sound/waveartist.h	Thu Jan  7 09:24:00 1999
+++ linux/drivers/sound/waveartist.h	Thu Jun 17 01:11:35 1999
@@ -2,16 +2,11 @@
 // def file for Rockwell RWA010 chip set, as installed in Corel 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 -u --recursive --new-file v2.3.6/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
--- v2.3.6/linux/drivers/usb/acm.c	Fri Jun  4 13:33:43 1999
+++ linux/drivers/usb/acm.c	Sun Jun 20 18:51:52 1999
@@ -50,7 +50,7 @@
 
 spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED;
 
-static int acm_irq(int state, void *__buffer, void *dev_id)
+static int acm_irq(int state, void *__buffer, int len, void *dev_id)
 {
 //	unsigned char *data = __buffer;
         struct acm_state *acm = &static_acm_state; 
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/audio.c linux/drivers/usb/audio.c
--- v2.3.6/linux/drivers/usb/audio.c	Fri Jun  4 13:33:43 1999
+++ linux/drivers/usb/audio.c	Sun Jun 20 18:51:52 1999
@@ -27,7 +27,7 @@
 };
 
 
-static int usb_audio_irq(int state, void *buffer, void *dev_id)
+static int usb_audio_irq(int state, void *buffer, int len, void *dev_id)
 {
 	struct usb_audio *aud = (struct usb_audio*) dev_id;
 	return 1;
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c
--- v2.3.6/linux/drivers/usb/cpia.c	Fri Jun  4 13:33:43 1999
+++ linux/drivers/usb/cpia.c	Sun Jun 20 18:51:52 1999
@@ -451,7 +451,7 @@
 	}
 }
 
-static int cpia_isoc_irq(int status, void *__buffer, void *dev_id)
+static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id)
 {
 	struct usb_cpia *cpia = dev_id;
 	struct usb_device *dev = cpia->dev;
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/hub.c linux/drivers/usb/hub.c
--- v2.3.6/linux/drivers/usb/hub.c	Tue Jun  8 10:52:26 1999
+++ linux/drivers/usb/hub.c	Sun Jun 20 18:51:52 1999
@@ -33,7 +33,7 @@
  * the low-level driver that it wants to be re-activated,
  * or zero to say "I'm done".
  */
-static int hub_irq(int status, void *__buffer, void *dev_id)
+static int hub_irq(int status, void *__buffer, int len, void *dev_id)
 {
 	struct usb_hub *hub = dev_id;
 	unsigned long flags;
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c
--- v2.3.6/linux/drivers/usb/keyboard.c	Tue Jun  8 10:52:26 1999
+++ linux/drivers/usb/keyboard.c	Sun Jun 20 18:51:52 1999
@@ -92,7 +92,7 @@
 }
 
 static int
-usb_kbd_irq(int state, void *buffer, void *dev_id)
+usb_kbd_irq(int state, void *buffer, int len, void *dev_id)
 {
     struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
     unsigned long *down = (unsigned long*) buffer;
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c
--- v2.3.6/linux/drivers/usb/mouse.c	Mon Jun  7 13:36:40 1999
+++ linux/drivers/usb/mouse.c	Sun Jun 20 18:51:52 1999
@@ -60,7 +60,7 @@
 
 spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;
 
-static int mouse_irq(int state, void *__buffer, void *dev_id)
+static int mouse_irq(int state, void *__buffer, int len, void *dev_id)
 {
 	signed char *data = __buffer;
 	/* finding the mouse is easy when there's only one */
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c
--- v2.3.6/linux/drivers/usb/ohci-hcd.c	Mon May 31 09:01:50 1999
+++ linux/drivers/usb/ohci-hcd.c	Sun Jun 20 18:51:52 1999
@@ -102,7 +102,7 @@
 	OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
 	OHCI_DEBUG( printk(" ret_status: %x\n", status); })
 	
- 	ret = handler(cc_to_status[status & 0xf], data, dev_id);
+ 	ret = handler(cc_to_status[status & 0xf], data, data_len, dev_id);
 	if(ret == 0) return 0; /* 0 .. do not requeue  */
 	if(status > 0) return -1; /* error occured do not requeue ? */
 	ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c
--- v2.3.6/linux/drivers/usb/ohci.c	Tue Jun  8 10:52:26 1999
+++ linux/drivers/usb/ohci.c	Sun Jun 20 18:55:52 1999
@@ -121,7 +121,7 @@
 	u32 new_dummy;
 
 	if (ed->tail_td == 0) {
-		printk("eek! an ED without a dummy_td\n");
+		printk(KERN_ERR "eek! an ED without a dummy_td\n");
 		return td;
 	}
 
@@ -234,8 +234,8 @@
 	 */
 	int_ed = &root_hub->ed[ms_to_ed_int(period)];
 #ifdef OHCI_DEBUG
-	printk("usb-ohci: Using INT ED queue %d for %dms period\n",
-			ms_to_ed_int(period), period);
+	printk(KERN_DEBUG "usb-ohci: Using INT ED queue %d for %dms period\n",
+	       ms_to_ed_int(period), period);
 #endif
 
 	spin_lock_irqsave(&ohci_edtd_lock, flags);
@@ -264,6 +264,21 @@
  */
 DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup);
 
+static void ohci_wait_sof(struct ohci_regs *regs)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&start_of_frame_wakeup, &wait);
+
+	/* clear the SOF interrupt status and enable it */
+	writel(OHCI_INTR_SF, &regs->intrstatus);
+	writel(OHCI_INTR_SF, &regs->intrenable);
+
+	schedule_timeout(HZ/10);
+
+	remove_wait_queue(&start_of_frame_wakeup, &wait);
+}
+
 /*
  * Guarantee that an ED is safe to be modified by the HCD (us).
  *
@@ -296,21 +311,11 @@
 	 * at least the next frame.
 	 */
 	if (virt_to_bus(ed) == readl(hw_listcurrent)) {
-		DECLARE_WAITQUEUE(wait, current);
-
 #ifdef OHCI_DEBUG
-		printk("Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed));
+		printk(KERN_INFO "Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed));
 #endif
+		ohci_wait_sof(regs);
 
-		add_wait_queue(&start_of_frame_wakeup, &wait);
-
-		/* clear the SOF interrupt status and enable it */
-		writel(OHCI_INTR_SF, &regs->intrstatus);
-		writel(OHCI_INTR_SF, &regs->intrenable);
-
-		schedule_timeout(HZ/10);
-
-		remove_wait_queue(&start_of_frame_wakeup, &wait);
 	}
 
 	return; /* The ED is now safe */
@@ -334,6 +339,7 @@
 
 	if (ed == NULL || !bus_ed)
 		return;
+	ed->status |= cpu_to_le32(OHCI_ED_SKIP);
 
 	switch (ed_type) {
 	case HCD_ED_CONTROL:
@@ -343,16 +349,10 @@
 		hw_listhead_p = &regs->ed_bulkhead;
 		break;
 	default:
-		printk("Unknown HCD ED type %d.\n", ed_type);
+		printk(KERN_ERR "Unknown HCD ED type %d.\n", ed_type);
 		return;
 	}
 
-	/*
-	 * Tell the controller to this skip ED and make sure it is not the
-	 * being accessed by the HC as we speak.
-	 */
-	ohci_wait_for_ed_safe(regs, ed, ed_type);
-
 	bus_cur = readl(hw_listhead_p);
 
 	if (bus_cur == 0)
@@ -364,7 +364,7 @@
 
 	/* if its the head ED, move the head */
 	if (bus_cur == bus_ed) {
-		writel(cur->next_ed, hw_listhead_p);
+		writel(le32_to_cpup(&cur->next_ed), hw_listhead_p);
 	} else if (cur->next_ed != 0) {
 		struct ohci_ed *prev;
 
@@ -373,7 +373,7 @@
 			prev = cur;
 			cur = bus_to_virt(le32_to_cpup(&cur->next_ed));
 
-			if (virt_to_bus(cur) == bus_ed) {
+			if (cur == ed) {
 				/* unlink from the list */
 				prev->next_ed = cur->next_ed;
 				break;
@@ -381,6 +381,11 @@
 		} while (cur->next_ed != 0);
 	}
 
+	/*
+	 * Make sure this ED is not being accessed by the HC as we speak.
+	 */
+	ohci_wait_for_ed_safe(regs, ed, ed_type);
+
 	/* clear any links from the ED for safety */
 	ed->next_ed = 0;
 
@@ -405,6 +410,68 @@
 	ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK);
 }
 
+/*
+ * Remove all the EDs which have a given device address from a list.
+ * Used when the device is unplugged.
+ * Returns 1 if anything was changed.
+ */
+static int ohci_remove_device_list(__u32 *headp, int devnum)
+{
+	struct ohci_ed *ed;
+	__u32 *prevp = headp;
+	int removed = 0;
+
+	while (*prevp != 0) {
+		ed = bus_to_virt(le32_to_cpup(prevp));
+		if ((le32_to_cpup(&ed->status) & OHCI_ED_FA) == devnum) {
+			/* set the controller to skip this one
+			   and remove it from the list */
+			ed->status |= cpu_to_le32(OHCI_ED_SKIP);
+			*prevp = ed->next_ed;
+			removed = 1;
+		} else {
+			prevp = &ed->next_ed;
+		}
+	}
+	wmb();
+
+	return removed;
+}
+
+/*
+ * Remove all the EDs for a given device from all lists.
+ */
+void ohci_remove_device(struct ohci *ohci, int devnum)
+{
+	unsigned long flags;
+	__u32 head;
+	struct ohci_regs *regs = ohci->regs;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
+
+	spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+	/* Control list */
+	head = cpu_to_le32(readl(&regs->ed_controlhead));
+	if (ohci_remove_device_list(&head, devnum))
+		writel(le32_to_cpup(&head), &regs->ed_controlhead);
+
+	/* Bulk list */
+	head = cpu_to_le32(readl(&regs->ed_bulkhead));
+	if (ohci_remove_device_list(&head, devnum))
+		writel(le32_to_cpup(&head), &regs->ed_bulkhead);
+
+	/* Interrupt/iso list */
+	head = cpu_to_le32(virt_to_bus(&root_hub->ed[ED_INT_32]));
+	ohci_remove_device_list(&head, devnum);
+
+	/*
+	 * Wait until the start of the next frame to ensure
+	 * that the HC has seen any changes.
+	 */
+	ohci_wait_sof(ohci->regs);
+
+	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+}
 
 /*
  *  Remove a TD from the given EDs TD list.
@@ -494,7 +561,7 @@
 		}
 	}
 
-	printk("usb-ohci: unable to allocate a TD\n");
+	printk(KERN_ERR "usb-ohci: unable to allocate a TD\n");
 	return NULL;
 } /* ohci_get_free_td() */
 
@@ -521,7 +588,7 @@
 		}
 	}
 
-	printk("usb-ohci: unable to allocate an ED\n");
+	printk(KERN_ERR "usb-ohci: unable to allocate an ED\n");
 	return NULL;
 } /* ohci_get_free_ed() */
 
@@ -594,12 +661,12 @@
 	struct ohci_td *dummy_td;
 
 	if (ed_head_td(ed) != ed_tail_td(ed))
-		printk("Reusing a non-empty ED %p!\n", ed);
+		printk(KERN_ERR "Reusing a non-empty ED %p!\n", ed);
 
 	if (!ed->tail_td) {
 		dummy_td = ohci_get_free_td(dev);
 		if (dummy_td == NULL) {
-			printk("Error allocating dummy TD for ED %p\n", ed);
+			printk(KERN_ERR "Error allocating dummy TD for ED %p\n", ed);
 			return NULL;	/* no dummy available! */
 		}
 		make_dumb_td(dummy_td);	/* flag it as a dummy */
@@ -607,7 +674,7 @@
 	} else {
 		dummy_td = bus_to_virt(ed_tail_td(ed));
 		if (!td_dummy(*dummy_td))
-			printk("ED %p's dummy %p is screwy\n", ed, dummy_td);
+			printk(KERN_ERR "ED %p's dummy %p is screwy\n", ed, dummy_td);
 	}
 
 	/* set the head TD to the dummy and clear the Carry & Halted bits */
@@ -650,13 +717,13 @@
 	/* Get an ED and TD */
 	interrupt_ed = ohci_get_free_ed(dev);
 	if (!interrupt_ed) {
-		printk("Out of EDs on device %p in ohci_request_irq\n", dev);
+		printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev);
 		return -1;
 	}
 
 	td = ohci_get_free_td(dev);
 	if (!td) {
-		printk("Out of TDs in ohci_request_irq\n");
+		printk(KERN_ERR "Out of TDs in ohci_request_irq\n");
 		ohci_free_ed(interrupt_ed);
 		return -1;
 	}
@@ -710,7 +777,7 @@
  *
  *  This function is called from the interrupt handler.
  */
-static int ohci_control_completed(int stats, void *buffer, void *dev_id)
+static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id)
 {
 	/* pass the TDs completion status back to control_msg */
 	if (dev_id) {
@@ -759,14 +826,14 @@
 	printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
 #endif
 	if (!control_ed) {
-		printk("usb-ohci: couldn't get ED for dev %p\n", dev);
+		printk(KERN_ERR "usb-ohci: couldn't get ED for dev %p\n", dev);
 		return -1;
 	}
 
 	/* get a TD to send this control message with */
 	setup_td = ohci_get_free_td(dev);
 	if (!setup_td) {
-		printk("usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev);
+		printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev);
 		ohci_free_ed(control_ed);
 		return -1;
 	}
@@ -799,7 +866,7 @@
 	/* allocate the next TD */
 	data_td = ohci_get_free_td(dev);
 	if (!data_td) {
-		printk("usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev);
+		printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev);
 		ohci_free_td(setup_td);
 		ohci_free_ed(control_ed);
 		return -1;
@@ -833,7 +900,7 @@
 
 		status_td = ohci_get_free_td(dev);  /* TODO check for NULL */
 		if (!status_td) {
-			printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev);
+			printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev);
 			ohci_free_td(setup_td);
 			ohci_free_td(data_td);
 			ohci_free_ed(control_ed);
@@ -918,15 +985,17 @@
 
 #ifdef OHCI_DEBUG
 	if (completion_status != 0) {
-		printk(KERN_ERR "ohci_control_msg: %s on cmd %x %x %x %x %x\n",
-		       cc_names[completion_status & 0xf], cmd->requesttype,
-		       cmd->request, cmd->value, cmd->index, cmd->length);
+		char *what = (completion_status < 0)? "timed out":
+			cc_names[completion_status & 0xf];
+		printk(KERN_ERR "ohci_control_msg: %s on pipe %x cmd %x %x %x %x %x\n",
+		       what, pipe, cmd->requesttype, cmd->request,
+		       cmd->value, cmd->index, cmd->length);
 	} else if (!usb_pipeout(pipe)) {
 		unsigned char *q = data;
 		int i;
-		printk(KERN_DEBUG "ctrl msg %x %x %x %x %x returned:",
+		printk(KERN_DEBUG "ctrl msg %x %x %x %x %x on pipe %x returned:",
 		       cmd->requesttype, cmd->request, cmd->value, cmd->index,
-		       cmd->length);
+		       cmd->length, pipe);
 		for (i = 0; i < len; ++i) {
 			if (i % 16 == 0)
 				printk("\n" KERN_DEBUG);
@@ -1001,8 +1070,12 @@
  */
 static int ohci_usb_deallocate(struct usb_device *usb_dev)
 {
-	kfree(usb_to_ohci(usb_dev));
-	kfree(usb_dev);
+	struct ohci_device *dev = usb_to_ohci(usb_dev);
+
+	ohci_remove_device(dev->ohci, usb_dev->devnum);
+
+	/* kfree(usb_to_ohci(usb_dev)); */
+	/* kfree(usb_dev); */
 	return 0;
 }
 
@@ -1042,13 +1115,13 @@
 
 	while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) {
 		if (!--timeout) {
-			printk("usb-ohci: USB HC reset timed out!\n");
+			printk(KERN_ERR "usb-ohci: USB HC reset timed out!\n");
 			return -1;
 		}
 		udelay(1);
 	}
 
-	printk(KERN_INFO "usb-ohci: HC %p reset.\n", ohci);
+	printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci);
 
 	return 0;
 } /* reset_hc() */
@@ -1080,7 +1153,8 @@
 	 * XXX Should fminterval also be set here?
 	 * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant)
 	 */
-	fminterval |= (0x2edf << 16);
+	/* fminterval |= (0x2edf << 16); */
+	fminterval = (10240 << 16) | 11999;
 	writel(fminterval, &ohci->regs->fminterval);
 	/* Start periodic transfers at 90% of fminterval (fmremaining
 	 * counts down; this will put them in the first 10% of the
@@ -1122,7 +1196,7 @@
 	/* Turn on power to the root hub ports (thanks Roman!) */
 	writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status );
 
-	printk("usb-ohci: host controller operational\n");
+	printk(KERN_INFO "usb-ohci: host controller operational\n");
 
 	return ret;
 } /* start_hc() */
@@ -1137,7 +1211,7 @@
 
 	/* Don't allow overflows. */
 	if (port >= MAX_ROOT_PORTS) {
-		printk("usb-ohci: bad port #%d in ohci_reset_port\n", port);
+		printk(KERN_ERR "usb-ohci: bad port #%d in ohci_reset_port\n", port);
 		port = MAX_ROOT_PORTS-1;
 	}
 
@@ -1152,7 +1226,7 @@
 	status = readl(&ohci->regs->roothub.portstatus[port]);
 	if (status & PORT_PRS) {
 		/* reset failed, try harder? */
-		printk("usb-ohci: port %d reset failed, retrying\n", port);
+		printk(KERN_ERR "usb-ohci: port %d reset failed, retrying\n", port);
 		writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);
 		wait_ms(50);
 	}
@@ -1340,7 +1414,7 @@
 		int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info));
 
 		if (td_dummy(*td))
-			printk("yikes! reaping a dummy TD\n");
+			printk(KERN_ERR "yikes! reaping a dummy TD\n");
 
 		/* FIXME: munge td->info into a future standard status format */
 
@@ -1382,7 +1456,7 @@
 
 		/* Check if TD should be re-queued */
 		if ((td->completed != NULL) &&
-		    (td->completed(cc, td->data, td->dev_id))) {
+		    (td->completed(cc, td->data, -1 /* XXX */, td->dev_id))) {
 			/* Mark the TD as active again:
 			 * Set the not accessed condition code
 			 * Reset the Error count
@@ -1598,14 +1672,14 @@
 	/* Get the number of ports on the root hub */
 	usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
 	if (usb->maxchild > MAX_ROOT_PORTS) {
-		printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);
+		printk(KERN_INFO "usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);
 		usb->maxchild = MAX_ROOT_PORTS;
 	}
 	if (usb->maxchild < 1) {
-		printk("usb-ohci: Less than one root hub port? Impossible!\n");
+		printk(KERN_ERR "usb-ohci: Less than one root hub port? Impossible!\n");
 		usb->maxchild = 1;
 	}
-	printk("usb-ohci: %d root hub ports found\n", usb->maxchild);
+	printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild);
 
 	/*
 	 * Initialize the ED polling "tree" (for simplicity's sake in
@@ -1650,14 +1724,13 @@
 	writel(0, &ohci->regs->ed_bulkhead);
 
 #ifdef OHCI_DEBUG
-	printk(KERN_INFO "alloc_ohci(): controller\n");
+	printk(KERN_DEBUG "alloc_ohci(): controller\n");
 	show_ohci_status(ohci);
 #endif
 
 #if 0
 	printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci);
 #endif
-printk("alloc_ohci done\n");
 
 	return ohci;
 } /* alloc_ohci() */
@@ -1722,7 +1795,7 @@
 	 * This thread doesn't need any user-level access,
 	 * so get rid of all of our resources..
 	 */
-	printk(KERN_INFO "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread);
+	printk(KERN_DEBUG "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread);
 	exit_mm(current);
 	exit_files(current);
 	exit_fs(current);
@@ -1735,7 +1808,7 @@
 	 * Damn the torpedoes, full speed ahead
 	 */
 	if (start_hc(ohci) < 0) {
-		printk("usb-ohci: failed to start the controller\n");
+		printk(KERN_ERR "usb-ohci: failed to start the controller\n");
 		release_ohci(ohci);
 		usb_deregister_bus(ohci->bus);
 		printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci);
@@ -1756,7 +1829,7 @@
 		writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
 #endif
 
-		printk(KERN_INFO "ohci-control thread sleeping\n");
+		printk(KERN_DEBUG "ohci-control thread sleeping\n");
 		interruptible_sleep_on(&ohci_configure);
 #ifdef CONFIG_APM
 		if (apm_resume) {
@@ -1796,7 +1869,7 @@
 	reset_hc(ohci);
 	release_ohci(ohci);
 	usb_deregister_bus(ohci->bus);
-	printk(KERN_INFO "ohci-control thread for 0x%p exiting\n", __ohci);
+	printk(KERN_DEBUG "ohci-control thread for 0x%p exiting\n", __ohci);
 
 	return 0;
 } /* ohci_control_thread() */
@@ -1891,7 +1964,7 @@
 		ohci->irq = irq;
 
 #ifdef OHCI_DEBUG
-		printk(KERN_INFO "usb-ohci: forking ohci-control thread for 0x%p\n", ohci);
+		printk(KERN_DEBUG "usb-ohci: forking ohci-control thread for 0x%p\n", ohci);
 #endif
 
 		/* fork off the handler */
@@ -1903,7 +1976,7 @@
 
 		retval = pid;
 	} else {
-		printk("usb-ohci: Couldn't allocate interrupt %d\n", irq);
+		printk(KERN_ERR "usb-ohci: Couldn't allocate interrupt %d\n", irq);
 	}
 	release_ohci(ohci);
 
@@ -1931,7 +2004,7 @@
 
 	/* no interrupt won't work... */
 	if (dev->irq == 0) {
-		printk("usb-ohci: no irq assigned? check your BIOS settings.\n");
+		printk(KERN_ERR "usb-ohci: no irq assigned? check your BIOS settings.\n");
 		return -ENODEV;
 	}
 
@@ -1944,14 +2017,14 @@
 	mem_base = (unsigned long) ioremap_nocache(mem_base, 4096);
 
 	if (!mem_base) {
-		printk("Error mapping OHCI memory\n");
+		printk(KERN_ERR "Error mapping OHCI memory\n");
 		return -EFAULT;
 	}
         MOD_INC_USE_COUNT;
 
 #ifdef OHCI_DEBUG
-	printk("usb-ohci: Warning! Gobs of debugging output has been enabled.\n");
-	printk("          Check your kern.debug logs for the bulk of it.\n");
+	printk(KERN_INFO "usb-ohci: Warning! Gobs of debugging output has been enabled.\n");
+	printk(KERN_INFO "          Check your kern.debug logs for the bulk of it.\n");
 #endif
 
 	if (found_ohci(dev->irq, (void *) mem_base) < 0) {
@@ -1978,11 +2051,11 @@
 	/*u8 type;*/
 
 	if (sizeof(struct ohci_device) > 4096) {
-		printk("usb-ohci: struct ohci_device to large\n");
+		printk(KERN_ERR "usb-ohci: struct ohci_device to large\n");
 		return -ENODEV;
 	}
 
-	printk("OHCI USB Driver loading\n");
+	printk(KERN_INFO "OHCI USB Driver loading\n");
 
 	retval = -ENODEV;
 	for (;;) {
@@ -2022,7 +2095,7 @@
 #	ifdef CONFIG_APM
 	apm_unregister_callback(&handle_apm_event);
 #	endif
-	printk("usb-ohci: module unloaded\n");
+	printk(KERN_ERR "usb-ohci: module unloaded\n");
 }
 
 int init_module(void){
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/uhci-debug.c linux/drivers/usb/uhci-debug.c
--- v2.3.6/linux/drivers/usb/uhci-debug.c	Mon May 31 09:01:50 1999
+++ linux/drivers/usb/uhci-debug.c	Sun Jun 20 11:43:42 1999
@@ -131,7 +131,7 @@
 #if 0
 	printk("    link = %p, element = %p\n", qh->link, qh->element);
 #endif
-	if(!qh->element) {
+	if(!(qh->element & ~0xF)) {
 		printk("    td 0 = NULL\n");
 		return;
 	}
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.6/linux/drivers/usb/uhci.c	Tue Jun  8 10:53:43 1999
+++ linux/drivers/usb/uhci.c	Sun Jun 20 18:54:01 1999
@@ -126,7 +126,7 @@
 
 		tmp = td->first;
 		printk("uhci_td_result() failed with status %x\n", status);
-		show_status(dev->uhci);
+		//show_status(dev->uhci);
 		do {
 			show_td(tmp);
 			if ((tmp->link & 1) || (tmp->link & 2))
@@ -422,7 +422,7 @@
 
 	    /* notify removal */
 
-	    td->completed(USB_ST_REMOVED, NULL, td->dev_id);
+	    td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id);
 
 	    /* this is DANGEROUS - not sure whether this is right */
 
@@ -645,7 +645,7 @@
  */
 static DECLARE_WAIT_QUEUE_HEAD(control_wakeup);
 
-static int uhci_control_completed(int status, void *buffer, void *dev_id)
+static int uhci_control_completed(int status, void *buffer, int len, void *dev_id)
 {
 	wake_up(&control_wakeup);
 	return 0;			/* Don't re-instate */
@@ -692,7 +692,7 @@
 //	show_status(dev->uhci);
 //	show_queues(dev->uhci);
 
-	schedule_timeout(HZ/10);
+	schedule_timeout(HZ*5);
 
 //	control should be empty here...	
 //	show_status(dev->uhci);
@@ -736,8 +736,7 @@
  * information, that's just ridiculously high. Most
  * control messages have just a few bytes of data.
  */
-static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe,
-			    devrequest *cmd, void *data, int len)
+static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
 {
 	struct uhci_device *dev = usb_to_uhci(usb_dev);
 	struct uhci_td *first, *td, *prevtd;
@@ -805,17 +804,18 @@
 	}
 
 	/*
-	 * Build the final TD for control status
+	 * Build the final TD for control status 
 	 */
 	destination ^= (0xE1 ^ 0x69);			/* OUT -> IN */
 	destination |= 1 << 19;				/* End in Data1 */
 
-	td->link = 1;					/* Terminate */
-	td->status = status | (1 << 24);		/* IOC */
+	td->backptr = &prevtd->link;
+	td->status = (status /* & ~(3 << 27) */) | (1 << 24);	/* no limit on final packet */
 	td->info = destination | (0x7ff << 21);		/* 0 bytes of data */
 	td->buffer = 0;
 	td->first = first;
-	td->backptr = &prevtd->link;
+	td->link = 1;					/* Terminate */
+
 
 	/* Start it up.. */
 	ret = uhci_run_control(dev, first, td);
@@ -841,7 +841,7 @@
 	}
 
 	if (uhci_debug && ret) {
-		__u8 *p = (__u8 *) cmd;
+		__u8 *p = cmd;
 
 		printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n",
 		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
@@ -860,7 +860,7 @@
  */
 static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup);
 
-static int uhci_bulk_completed(int status, void *buffer, void *dev_id)
+static int uhci_bulk_completed(int status, void *buffer, int len, void *dev_id)
 {
 	wake_up(&bulk_wakeup);
 	return 0;			/* Don't re-instate */
@@ -908,10 +908,11 @@
 //	show_status(dev->uhci);
 //	show_queues(dev->uhci);
 
-	schedule_timeout(HZ/10);
+	schedule_timeout(HZ*5);
 //	show_status(dev->uhci);
 //	show_queues(dev->uhci);
 
+	//show_queue(first->qh);
 	remove_wait_queue(&bulk_wakeup, &wait);
 
 	/* Clean up in case it failed.. */
@@ -1243,6 +1244,7 @@
 {
 	struct list_head *head = &uhci->interrupt_list;
 	struct list_head *tmp;
+	int status;
 
 	spin_lock(&irqlist_lock);
 	tmp = head->next;
@@ -1252,19 +1254,22 @@
 
 		next = tmp->next;
 
-		if (!(td->status & (1 << 23))) {	/* No longer active? */
+		if (!((status = td->status) & (1 << 23)) ||  /* No longer active? */
+		    ((td->qh->element & ~15) && 
+		      !((status = uhci_link_to_td(td->qh->element)->status) & (1 <<23)) &&
+		      (status & 0x760000) /* is in error state (Stall, db, babble, timeout, bitstuff) */)) {	
 			/* remove from IRQ list */
 			__list_del(tmp->prev, next);
 			INIT_LIST_HEAD(tmp);
-			if (td->completed(uhci_map_status((td->status & 0xff)>> 16, 0),
-					  bus_to_virt(td->buffer), td->dev_id)) {
+			if (td->completed(uhci_map_status(status, 0), bus_to_virt(td->buffer), -1, td->dev_id)) {
 				list_add(&td->irq_list, &uhci->interrupt_list);
 
 				if (!(td->status & (1 << 25))) {
 					struct uhci_qh *interrupt_qh = td->qh;
 
 					usb_dotoggle(td->dev, usb_pipeendpoint(td->info));
-					td->info |= 1 << 19; /* toggle between data0 and data1 */
+					td->info &= ~(1 << 19); /* clear data toggle */
+					td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info)) << 19; /* toggle between data0 and data1 */
 					td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24);	/* active */
 
 					/* Remove then readd? Is that necessary */
@@ -1283,7 +1288,7 @@
 			/* If completed wants to not reactivate, then it's */
 			/* responsible for free'ing the TD's and QH's */
 			/* or another function (such as run_control) */
-		}
+		} 
 		tmp = next;
 	}
 	spin_unlock(&irqlist_lock);
@@ -1563,6 +1568,7 @@
 {
 	struct uhci *uhci = (struct uhci *)__uhci;
 	struct uhci_device * root_hub =usb_to_uhci(uhci->bus->root_hub);
+
 	lock_kernel();
 	request_region(uhci->io_addr, 32, "usb-uhci");
 
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- v2.3.6/linux/drivers/usb/usb-core.c	Mon Jun  7 20:04:01 1999
+++ linux/drivers/usb/usb-core.c	Wed Jun 16 19:26:27 1999
@@ -53,7 +53,7 @@
 		usb_acm_init();
 #	endif
 #	ifdef CONFIG_USB_PRINTER
-		usb_print_init();
+		usb_printer_init();
 #	endif
 #	ifdef CONFIG_USB_CPIA
 		usb_cpia_init();
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb.h linux/drivers/usb/usb.h
--- v2.3.6/linux/drivers/usb/usb.h	Tue Jun  8 23:04:17 1999
+++ linux/drivers/usb/usb.h	Sun Jun 20 18:51:52 1999
@@ -242,10 +242,12 @@
  *         until we come up with a common meaning.
  *     void *buffer - This is a pointer to the data used in this
  *         USB transfer.
+ *     int length - This is the number of bytes transferred in or out
+ *         of the buffer by this transfer.  (-1 = unknown/unsupported)
  *     void *dev_id - This is a user defined pointer set when the IRQ
  *         is requested that is passed back.
  */
-typedef int (*usb_device_irq)(int, void *, void *);
+typedef int (*usb_device_irq)(int, void *, int, void *);
 
 struct usb_operations {
 	struct usb_device *(*allocate)(struct usb_device *);
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb_scsi.c linux/drivers/usb/usb_scsi.c
--- v2.3.6/linux/drivers/usb/usb_scsi.c	Mon Jun  7 20:04:01 1999
+++ linux/drivers/usb/usb_scsi.c	Sun Jun 20 18:51:52 1999
@@ -74,7 +74,9 @@
 	__u8			ep_int;			/* interrupt . */
 	__u8			subclass;		/* as in overview */
 	__u8			protocol;		/* .............. */
+	__u8			attention_done;		/* force attention on first command */
 	int (*pop)(Scsi_Cmnd *);			/* protocol specific do cmd */
+	int (*pop_reset)(struct us_data *);		/* ................. device reset */
 	GUID(guid);					/* unique dev id */
 	struct Scsi_Host	*host;			/* our dummy host data */
 	Scsi_Host_Template	*htmplt;		/* own host template */
@@ -142,6 +144,9 @@
 
 	    /* we want to retry if the device reported NAK */
 	    if (result == USB_ST_TIMEOUT) {
+		if (partial != this_xfer) {
+		    return 0;	/* I do not like this */
+		}
 		if (!maxtry--)
 		    break;
 		this_xfer -= partial;
@@ -150,6 +155,11 @@
 		/* short data - assume end */
 		result = USB_ST_DATAUNDERRUN;
 		break;
+	    } else if (result == USB_ST_STALL && us->protocol == US_PR_CB) {
+		if (!maxtry--)
+		    break;
+		this_xfer -= partial;
+		buf += partial;
 	    } else
 		break;
 	} while ( this_xfer );
@@ -216,27 +226,57 @@
 
 }
 
-static int pop_CBI_irq(int state, void *buffer, void *dev_id)
+static int pop_CBI_irq(int state, void *buffer, int len, void *dev_id)
 {
     struct us_data *us = (struct us_data *)dev_id;
 
     if (state != USB_ST_REMOVED) {
 	us->ip_data = *(__u16 *)buffer;
-	us->ip_wanted = 0;
+	US_DEBUGP("Interrupt Status %x\n", us->ip_data);
     }
-    wake_up(&us->ip_waitq);
+    if (us->ip_wanted)
+	wake_up(&us->ip_waitq);
+    us->ip_wanted = 0;
 
     /* we dont want another interrupt */
 
     return 0;
 }
+
+static int pop_CB_reset(struct us_data *us)
+{
+    unsigned char cmd[12];
+    devrequest dr;
+    int result;
+
+    dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+    dr.request = US_CBI_ADSC;
+    dr.value = 0;
+    dr.index = us->pusb_dev->ifnum;
+    dr.length = 12;
+    memset(cmd, -1, sizeof(cmd));
+    cmd[0] = SEND_DIAGNOSTIC;
+    cmd[1] = 4;
+    us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
+					usb_sndctrlpipe(us->pusb_dev,0),
+					&dr, cmd, 12);
+
+    usb_clear_halt(us->pusb_dev, us->ep_in | 0x80);
+    usb_clear_halt(us->pusb_dev, us->ep_out);
+
+    /* long wait for reset */
+
+    schedule_timeout(HZ*5);
+    return 0;
+}
+
 static int pop_CB_command(Scsi_Cmnd *srb)
 {
     struct us_data *us = (struct us_data *)srb->host_scribble;
     devrequest dr;
     unsigned char cmd[16];
     int result;
-    int retry = 1;
+    int retry = 5;
     int done_start = 0;
 
     while (retry--) {
@@ -279,7 +319,8 @@
 	    result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
 						  usb_sndctrlpipe(us->pusb_dev,0),
 						  &dr, cmd, us->fixedlength);
-	    if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) {
+	    if (!done_start && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/)
+		 && cmd[0] == TEST_UNIT_READY && result) {
 		/* as per spec try a start command, wait and retry */
 
 		done_start++;
@@ -302,35 +343,47 @@
     return result;
 }
 
-/* Protocol command handlers */
+/*
+ * Control/Bulk status handler
+ */
 
-static int pop_CBI(Scsi_Cmnd *srb)
+static int pop_CB_status(Scsi_Cmnd *srb)
 {
     struct us_data *us = (struct us_data *)srb->host_scribble;
     int result;
+    __u8 status[2];
+    devrequest dr;
+    int retry = 5;
 
-    /* run the command */
-
-    if ((result = pop_CB_command(srb))) {
-	US_DEBUGP("CBI command %x\n", result);
-	if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
-	    return (DID_OK << 16) | 2;
-	return DID_ABORT << 16;
-    }
-
-    /* transfer the data */
+    switch (us->protocol) {
+    case US_PR_CB:
+	/* get from control */
 
-    if (us_transfer_length(srb)) {
-	result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
-	if (result && result != USB_ST_DATAUNDERRUN) {
-	    US_DEBUGP("CBI  transfer %x\n", result);
+	while (retry--) {
+	    dr.requesttype = 0x80 | USB_TYPE_STANDARD | USB_RT_DEVICE;
+	    dr.request = USB_REQ_GET_STATUS;
+	    dr.index = 0;
+	    dr.value = 0;
+	    dr.length = 2;
+	    result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
+						  usb_rcvctrlpipe(us->pusb_dev,0),
+						  &dr, status, sizeof(status));
+	    if (result != USB_ST_TIMEOUT)
+		break;
+	}
+	if (result) {
+	    US_DEBUGP("Bad AP status request %d\n", result);
 	    return DID_ABORT << 16;
 	}
-    }
-
-    /* get status */
+	US_DEBUGP("Got AP status %x %x\n", status[0], status[1]);
+	if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && 
+	    ( (status[0] & ~3) || status[1]))
+	    return (DID_OK << 16) | 2;
+	else
+	    return DID_OK << 16;
+	break;
 
-    if (us->protocol == US_PR_CBI) {
+    case US_PR_CBI:
 	/* get from interrupt pipe */
 
 	/* add interrupt transfer, marked for removal */
@@ -367,12 +420,48 @@
 	    return DID_ABORT << 16;
 	}
 	return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0);
-    } else {
-       /* get from where? */
     }
     return DID_ERROR << 16;
 }
 
+/* Protocol command handlers */
+
+static int pop_CBI(Scsi_Cmnd *srb)
+{
+    struct us_data *us = (struct us_data *)srb->host_scribble;
+    int result;
+
+    /* run the command */
+
+    if ((result = pop_CB_command(srb))) {
+	US_DEBUGP("CBI command %x\n", result);
+	if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)	{
+	    return (DID_OK << 16) | 2;
+	}
+	return DID_ABORT << 16;
+    }
+
+    /* transfer the data */
+
+    if (us_transfer_length(srb)) {
+	result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+	if (result && result != USB_ST_DATAUNDERRUN) {
+	    US_DEBUGP("CBI  transfer %x\n", result);
+	    return DID_ABORT << 16;
+	} else if (result == USB_ST_DATAUNDERRUN) {
+	    return DID_OK << 16;
+	}
+    } else {
+	if (!result) {
+	    return DID_OK << 16;
+	}
+    }
+
+    /* get status */
+
+    return pop_CB_status(srb);
+}
+
 static int pop_Bulk_reset(struct us_data *us)
 {
     devrequest dr;
@@ -380,21 +469,20 @@
 
     dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
     dr.request = US_BULK_RESET;
-    dr.value = US_BULK_RESET_SOFT;
+    dr.value = US_BULK_RESET_HARD;
     dr.index = 0;
     dr.length = 0;
 
-    US_DEBUGP("Bulk soft reset\n");
     result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
-    if (result) {
-	US_DEBUGP("Bulk soft reset failed %d\n", result);
-	dr.value = US_BULK_RESET_HARD;
-	result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
-	if (result)
-	    US_DEBUGP("Bulk hard reset failed %d\n", result);
-    }
+    if (result)
+	US_DEBUGP("Bulk hard reset failed %d\n", result);
     usb_clear_halt(us->pusb_dev, us->ep_in | 0x80);
     usb_clear_halt(us->pusb_dev, us->ep_out);
+
+    /* long wait for reset */
+
+    schedule_timeout(HZ*5);
+
     return result;
 }
 /*
@@ -453,8 +541,6 @@
 
     stall = 0;
     do {
-	//usb_settoggle(us->pusb_dev, us->ep_in, 0);	/* AAARgh!! */
-	US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in));
 	result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
 			 usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, 
 						US_BULK_CS_WRAP_LEN, &partial);
@@ -564,6 +650,9 @@
     struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
     US_DEBUGP("Command wakeup\n");
+    if (us->srb) {
+	/* busy */
+    }
     srb->host_scribble = (unsigned char *)us;
     us->srb = srb;
     srb->scsi_done = done;
@@ -581,9 +670,12 @@
     return 0;
 }
 
-static int us_device_reset( Scsi_Cmnd *srb )
+static int us_bus_reset( Scsi_Cmnd *srb )
 {
-    return 0;
+    struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+    us->pop_reset(us);
+    return SUCCESS;
 }
 
 static int us_host_reset( Scsi_Cmnd *srb )
@@ -591,10 +683,6 @@
     return 0;
 }
 
-static int us_bus_reset( Scsi_Cmnd *srb )
-{
-    return 0;
-}
 
 #undef SPRINTF
 #define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
@@ -623,9 +711,9 @@
     if (inout)
 	return length;
 
-    if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer)))
+    if (!us->pusb_dev || !(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer)))
 	vendor = "?";
-    if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct)))
+    if (!us->pusb_dev || !(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct)))
 	product = "?";
 
     switch (us->protocol) {
@@ -677,7 +765,7 @@
     us_queuecommand,
     NULL,			/* eh_strategy */
     us_abort,
-    us_device_reset,
+    us_bus_reset,
     us_bus_reset,
     us_host_reset,
     NULL,			/* abort */
@@ -695,6 +783,25 @@
     TRUE			/* emulated */
 };
 
+static unsigned char sense_notready[] = {
+    0x70,			/* current error */
+    0x00,
+    0x02,			/* not ready */
+    0x00,
+    0x00,
+    10,				/* additional length */
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x04,			/* not ready */
+    0x03,			/* manual intervention */
+    0x00,
+    0x00,
+    0x00,
+    0x00
+};
+
 static int usbscsi_control_thread(void * __us)
 {
     struct us_data *us = (struct us_data *)__us;
@@ -710,7 +817,7 @@
     exit_files(current);
     //exit_fs(current);
 
-    sprintf(current->comm, "usbscsi%d", us->host_no);
+    sprintf(current->comm, "usbscsi%d", us->host_number);
     
     unlock_kernel();
 
@@ -727,18 +834,160 @@
 
 	    switch (action) {
 	    case US_ACT_COMMAND       :
-		if (!us->pusb_dev || us->srb->target || us->srb->lun) {
+		if (us->srb->target || us->srb->lun) {
 		    /* bad device */
 		    US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
 		    us->srb->result = DID_BAD_TARGET << 16;
+		} else if (!us->pusb_dev) {
+
+		    /* our device has gone - pretend not ready */
+
+		    if (us->srb->cmnd[0] == REQUEST_SENSE) {
+			memcpy(us->srb->request_buffer, sense_notready, sizeof(sense_notready));
+			us->srb->result = DID_OK << 16;
+		    } else {
+			us->srb->result = (DID_OK << 16) | 2;
+		    }
 		} else {
 		    US_DEBUG(us_show_command(us->srb));
+
+		    /* check for variable length - do properly if so */
+
 		    if (us->filter && us->filter->command)
 		        us->srb->result = us->filter->command(us->fdata, us->srb);
-		    else
+		    else if (us->srb->cmnd[0] == START_STOP &&
+			     us->pusb_dev->descriptor.idProduct == 0x0001 &&
+			     us->pusb_dev->descriptor.idVendor == 0x04e6)
+			us->srb->result = DID_OK << 16;
+		    else {
+			unsigned int savelen = us->srb->request_bufflen;
+			unsigned int saveallocation;
+
+			switch (us->srb->cmnd[0]) {
+			case REQUEST_SENSE:
+			    if (us->srb->request_bufflen > 18)
+				us->srb->request_bufflen = 18;
+			    else
+				break;
+			    saveallocation = us->srb->cmnd[4];
+                            us->srb->cmnd[4] = 18;
+			    break;
+
+			case INQUIRY:
+			    if (us->srb->request_bufflen > 36)
+				us->srb->request_bufflen = 36;
+			    else
+				break;
+			    saveallocation = us->srb->cmnd[4];
+                            us->srb->cmnd[4] = 36;
+			    break;
+
+			case MODE_SENSE:
+			    if (us->srb->request_bufflen > 4)
+				us->srb->request_bufflen = 4;
+			    else
+				break;
+			    saveallocation = us->srb->cmnd[4];
+                            us->srb->cmnd[4] = 4;
+			    break;
+
+			case LOG_SENSE:
+			case MODE_SENSE_10:
+			    if (us->srb->request_bufflen > 8)
+				us->srb->request_bufflen = 8;
+			    else
+				break;
+			    saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
+                            us->srb->cmnd[7] = 0;
+                            us->srb->cmnd[8] = 8;
+			    break;
+
+			default:
+			    break;
+			}
 		        us->srb->result = us->pop(us->srb);
+
+			if (savelen != us->srb->request_bufflen &&
+			    us->srb->result == (DID_OK << 16)) {
+			    unsigned char *p = (unsigned char *)us->srb->request_buffer;
+			    unsigned int length;
+
+			    /* set correct length and retry */
+			    switch (us->srb->cmnd[0]) {
+			    case REQUEST_SENSE:
+				/* simply return 18 bytes */
+				p[7] = 10;
+				length = us->srb->request_bufflen;;
+				break;
+
+			    case INQUIRY:
+				length = p[4] + 5 > savelen ? savelen : p[4] + 5;
+				us->srb->cmnd[4] = length;
+				break;
+
+			    case MODE_SENSE:
+				length = p[0] + 4 > savelen ? savelen : p[0] + 4;
+				us->srb->cmnd[4] = 4;
+				break;
+
+			    case LOG_SENSE:
+				length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
+				us->srb->cmnd[7] = length >> 8;
+				us->srb->cmnd[8] = length;
+				break;
+
+			    case MODE_SENSE_10:
+				length = ((p[0] << 8) + p[1]) + 8 > savelen ? savelen : ((p[0] << 8) + p[1]) + 8;
+				us->srb->cmnd[7] = length >> 8;
+				us->srb->cmnd[8] = length;
+				break;
+			    }
+
+			    US_DEBUGP("Old/New length = %d/%d\n", savelen, length);
+
+			    if (us->srb->request_bufflen != length) {
+				us->srb->request_bufflen = length;
+				us->srb->result = us->pop(us->srb);
+			    }
+			    /* reset back to original values */
+
+			    us->srb->request_bufflen = savelen;
+			    switch (us->srb->cmnd[0]) {
+			    case REQUEST_SENSE:
+			    case INQUIRY:
+			    case MODE_SENSE:
+				us->srb->cmnd[4] = saveallocation;
+				break;
+
+			    case LOG_SENSE:
+			    case MODE_SENSE_10:
+				us->srb->cmnd[7] = saveallocation >> 8;
+				us->srb->cmnd[8] = saveallocation;
+				break;
+			    }
+			}
+			/* force attention on first command */
+			if (!us->attention_done) {
+			    if (us->srb->cmnd[0] == REQUEST_SENSE) {
+				if (us->srb->result == (DID_OK << 16)) {
+				    unsigned char *p = (unsigned char *)us->srb->request_buffer;
+
+				    us->attention_done = 1;
+				    if ((p[2] & 0x0f) != UNIT_ATTENTION) {
+					p[2] = UNIT_ATTENTION;
+					p[12] = 0x29;	/* power on, reset or bus-reset */
+					p[13] = 0;
+				    }
+				}
+			    } else if (us->srb->cmnd[0] != INQUIRY &&
+				       us->srb->result == (DID_OK << 16)) {
+				us->srb->result |= 2;	/* force check condition */
+			    }
+			}
+		    }
 		}
 		us->srb->scsi_done(us->srb);
+		us->srb = NULL;
 		break;
 
 	    case US_ACT_ABORT         :
@@ -820,7 +1069,7 @@
 	if (dev->descriptor.idVendor == 0x04e6 &&
 	    dev->descriptor.idProduct == 0x0001) {
 	    /* shuttle E-USB */
-	    protocol = US_PR_ZIP;
+	    protocol = US_PR_CB;
 	    subclass = US_SC_8070;	/* an assumption */
 	} else if (dev->descriptor.bDeviceClass != 0 ||
 	    dev->config->altsetting->interface->bInterfaceClass != 8 ||
@@ -835,11 +1084,15 @@
 	    usb_string(dev, dev->descriptor.iSerialNumber) ) {
 	    make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
 		      usb_string(dev, dev->descriptor.iSerialNumber));
-	    for (ss = us_list; ss; ss = ss->next) {
-		if (GUID_EQUAL(guid, ss->guid))	{
-		    US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
-		    break;
-		}
+	} else {
+	    make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+		      "0");
+	}
+	for (ss = us_list; ss; ss = ss->next) {
+	    if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid))	{
+		US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+		flags = ss->flags;
+		break;
 	    }
 	}
     }
@@ -865,6 +1118,7 @@
 	ss->subclass = interface->bInterfaceSubClass;
 	ss->protocol = interface->bInterfaceProtocol;
     }
+    ss->attention_done = 0;
 
     /* set the protocol op */
 
@@ -873,16 +1127,19 @@
     case US_PR_CB:
 	US_DEBUGPX("Control/Bulk\n");
 	ss->pop = pop_CBI;
+	ss->pop_reset = pop_CB_reset;
 	break;
 
     case US_PR_CBI:
 	US_DEBUGPX("Control/Bulk/Interrupt\n");
 	ss->pop = pop_CBI;
+	ss->pop_reset = pop_CB_reset;
 	break;
 
     default:
 	US_DEBUGPX("Bulk\n");
 	ss->pop = pop_Bulk;
+	ss->pop_reset = pop_Bulk_reset;
 	break;
     }
 
@@ -907,6 +1164,7 @@
     /* exit if strange looking */
 
     if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) ||
+	usb_set_interface(dev, interface->bInterfaceNumber, 0) ||
 	!ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
 	US_DEBUGP("Problems with device\n");
 	if (ss->host) {
@@ -933,13 +1191,8 @@
 
 	/* make unique id if possible */
 
-	if (dev->descriptor.iSerialNumber && 
-	    usb_string(dev, dev->descriptor.iSerialNumber) ) {
-	    make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
-		      usb_string(dev, dev->descriptor.iSerialNumber));
-	}
-
 	US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+	memcpy(ss->guid, guid, sizeof(guid));
 
 	/* set class specific stuff */
 
@@ -986,8 +1239,29 @@
 
 
 	(struct us_data *)htmplt->proc_dir = ss;
-	if (ss->protocol == US_PR_CBI)
+
+	if (dev->descriptor.idVendor == 0x04e6 &&
+	    dev->descriptor.idProduct == 0x0001) {
+	    devrequest dr;
+	    __u8 qstat[2];
+
+	    /* shuttle E-USB */
+	    dr.requesttype = 0xC0;
+	    dr.request = 1;
+	    dr.index = 0;
+	    dr.value = 0;
+	    dr.length = 0;
+	    ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2);
+	    US_DEBUGP("C0 status %x %x\n", qstat[0], qstat[1]);
+	    init_waitqueue_head(&ss->ip_waitq);
+	    ss->pusb_dev->bus->op->request_irq(ss->pusb_dev, 
+						usb_rcvctrlpipe(ss->pusb_dev, ss->ep_int),
+						pop_CBI_irq, 0, (void *)ss);
+	    interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*5);
+
+	} else if (ss->protocol == US_PR_CBI)
 	    init_waitqueue_head(&ss->ip_waitq);
+
 
 	/* start up our thread */
 
diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb_scsi_debug.c linux/drivers/usb/usb_scsi_debug.c
--- v2.3.6/linux/drivers/usb/usb_scsi_debug.c	Mon Jun  7 20:04:01 1999
+++ linux/drivers/usb/usb_scsi_debug.c	Sun Jun 20 11:43:42 1999
@@ -95,7 +95,7 @@
     case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
     case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
     case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
-    default: what = "??"; break;
+    default: break;
     }
     printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len);
     printk(KERN_DEBUG USB_SCSI "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
diff -u --recursive --new-file v2.3.6/linux/drivers/video/acornfb.c linux/drivers/video/acornfb.c
--- v2.3.6/linux/drivers/video/acornfb.c	Tue May 11 16:30:45 1999
+++ linux/drivers/video/acornfb.c	Thu Jun 17 01:11:35 1999
@@ -254,7 +254,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;
diff -u --recursive --new-file v2.3.6/linux/drivers/video/cyber2000fb.c linux/drivers/video/cyber2000fb.c
--- v2.3.6/linux/drivers/video/cyber2000fb.c	Tue May 11 16:30:45 1999
+++ linux/drivers/video/cyber2000fb.c	Thu Jun 17 01:11:35 1999
@@ -121,7 +121,7 @@
 		1600, 1200,
 		{
 			0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
-			0x00, 0x40,
+ 			0x00, 0x40,
 			0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
 		},
 		0x1f,
@@ -154,54 +154,54 @@
 	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_crtcw(0x11, 0x0b);
+	cyber2000_attrw(0x11, 0x00);
 
-	cyber2000_seqw(0x01, 0x00);
+	cyber2000_seqw(0x00, 0x01);
 	cyber2000_seqw(0x01, 0x01);
-	cyber2000_seqw(0x0f, 0x02);
-	cyber2000_seqw(0x00, 0x03);
-	cyber2000_seqw(0x0e, 0x04);
+	cyber2000_seqw(0x02, 0x0f);
 	cyber2000_seqw(0x03, 0x00);
+	cyber2000_seqw(0x04, 0x0e);
+	cyber2000_seqw(0x00, 0x03);
 
 	for (i = 0; i < sizeof(crtc_idx); i++)
-		cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]);
+		cyber2000_crtcw(crtc_idx[i], res->crtc_regs[i]);
 
 	for (i = 0x0a; i < 0x10; i++)
-		cyber2000_crtcw(0, i);
+		cyber2000_crtcw(i, 0);
 
-	cyber2000_crtcw(0xff, 0x18);
+	cyber2000_crtcw(0x18, 0xff);
 
 	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);
+	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);
 
 	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);
+	cyber2000_attrw(0x10, 0x01);
+	cyber2000_attrw(0x11, 0x00);
+	cyber2000_attrw(0x12, 0x0f);
+	cyber2000_attrw(0x13, 0x00);
+	cyber2000_attrw(0x14, 0x00);
 
 	for (i = 0; i < sizeof(igs_regs); i += 2)
-		cyber2000_grphw(igs_regs[i+1], igs_regs[i]);
+		cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
 
-	cyber2000_grphw(res->crtc_ofl, 0x11);
+	cyber2000_grphw(0x11, res->crtc_ofl);
 
 	for (i = 0; i < 4; i += 1)
-		cyber2000_grphw(res->clk_regs[i], 0xb0 + i);
+		cyber2000_grphw(0xb0 + i, res->clk_regs[i]);
 
-	cyber2000_grphw(0x01, 0x90);
-	cyber2000_grphw(0x80, 0xb9);
-	cyber2000_grphw(0x00, 0xb9);
+	cyber2000_grphw(0x90, 0x01);
+	cyber2000_grphw(0xb9, 0x80);
+	cyber2000_grphw(0xb9, 0x00);
 
 	cyber2000_outb(0x56, 0x3ce);
 	i = cyber2000_inb(0x3cf);
@@ -311,6 +311,7 @@
 	cyber2000_outw(height, 0xbf062);
 
 	switch (p->var.bits_per_pixel) {
+	case 15:
 	case 16:
 		bgx = ((u16 *)p->dispsw_data)[bgx];
 	case 8:
@@ -415,14 +416,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,36 +478,123 @@
 	return 0;
 }
 
-static int cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+static void cyber2000fb_calculate_timing(unsigned char *v, struct fb_var_screeninfo *var)
 {
-	int width = var->xres_virtual;
-	int scr_pitch, fetchrow;
-	int i;
-	char b, col;
+	int Htotal, Hdispend, Hblankstart, Hblankend, Hsyncstart, Hsyncend;
+	int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
+#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
+
+	Hdispend    = var->xres;
+	Hsyncstart  = var->xres + var->right_margin;
+	Hsyncend    = var->xres + var->right_margin + var->hsync_len;
+	Htotal      = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+
+	Hblankstart = var->xres;
+	Hblankend   = Htotal - 4*8;
+
+	Vdispend    = var->yres;
+	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;
+
+	Vblankstart = var->yres + 7;
+	Vblankend   = Vtotal - 11;
+
+	Hdispend    >>= 3;
+	Hsyncstart  >>= 3;
+	Hsyncend    >>= 3;
+	Htotal      >>= 3;
+	Hblankstart >>= 3;
+	Hblankend   >>= 3;
+
+	Htotal      -= 5;
+	Hdispend    -= 1;
+	Vtotal	    -= 2;
+	Vdispend    -= 1;
+	Vblankstart -= 1;
+	Vblankend   -= 1;
+
+	v[0]  = Htotal;
+	v[1]  = Hdispend;
+	v[2]  = Hblankstart;
+	v[3]  = BIT(Hblankend,  0, 0x1f,  0) |
+		BIT(1,          0, 0x01,  7);
+	v[4]  = Hsyncstart;
+	v[5]  = BIT(Hsyncend,   0, 0x1f,  0) |
+	        BIT(Hblankend,  5, 0x01,  7);
+
+	v[6]  = Vtotal;
+	v[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);
+	v[8]  = 0;
+	v[9]  = BIT(0,          0, 0x1f,  0) |
+	        BIT(Vblankstart,9, 0x01,  5) |
+		BIT(1,          0, 0x01,  6);
+	v[10] = Vsyncstart;
+	v[11] = BIT(Vsyncend,   0, 0x0f,  0) |
+		BIT(1,          0, 0x01,  7);
+	v[12] = Vdispend;
+	v[14] = 0;
+	v[15] = Vblankstart;
+	v[16] = Vblankend;
+	v[17] = 0xe3;
+
+	/* overflow - graphics reg 0x11 */
+	v[18] = BIT(Vtotal,     10, 0x01,  0) | /* guess */
+		BIT(Vdispend,   10, 0x01,  1) |
+		BIT(Vsyncstart, 10, 0x01,  2) | /* guess */
+		BIT(Vblankstart,10, 0x01,  3) | /* guess */
+		BIT(Hblankend,   6, 0x01,  4);  /* guess */
+}
+
+static void cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+{
+	unsigned int width = var->xres_virtual;
+	unsigned int scr_pitch, fetchrow, i;
+	char b, graph_r77, crtc[32];
 
 	switch (var->bits_per_pixel) {
 	case 8:	/* PSEUDOCOLOUR, 256 */
 		b = 0;
-		col = 1;
-		scr_pitch = var->xres_virtual / 8;
+		graph_r77 = 1;
+		scr_pitch = width;
+		break;
+
+	case 15:/* DIRECTCOLOUR, 32k */
+		b = 1;
+		graph_r77 = 6;
+		scr_pitch = width * 2;
 		break;
 
 	case 16:/* DIRECTCOLOUR, 64k */
 		b = 1;
-		col = 2;
-		scr_pitch = var->xres_virtual / 8 * 2;
+		graph_r77 = 2;
+		scr_pitch = width * 2;
 		break;
+
 	case 24:/* TRUECOLOUR, 16m */
 		b = 2;
-		col = 4;
-		scr_pitch = var->xres_virtual / 8 * 3;
+		graph_r77 = 4;
 		width *= 3;
+		scr_pitch = width;
 		break;
 
 	default:
-		return 1;
+		return;
 	}
 
+	width -= 1;
+	scr_pitch >>= 3;
+	fetchrow = scr_pitch + 1;
+
+	cyber2000fb_calculate_timing(crtc, var);
+
 	for (i = 0; i < NUM_TOTAL_MODES; i++)
 		if (var->xres == cyber2000_res[i].xres &&
 		    var->yres == cyber2000_res[i].yres)
@@ -502,30 +603,41 @@
 	if (i < NUM_TOTAL_MODES)
 		cyber2000_init_hw(cyber2000_res + i);
 
-	fetchrow = scr_pitch + 1;
+	crtc[13] = scr_pitch;
 
-	debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n",
-		     scr_pitch, fetchrow, col, b);
+	/*
+	 * reprogram the CRTC with the values we calculated
+	 * above.  This should be cleaned up once we're
+	 * confident that we're generating the correct
+	 * values.  Disable this if you're having problems,
+	 * and report the values obtained from the kernel
+	 * messages.
+	 */
+#if 1
+	cyber2000_crtcw(0x11, 0x0b);
+	for (i = 0; i < sizeof(crtc_idx); i++)
+		cyber2000_crtcw(crtc_idx[i], crtc[i]);
+#else
+	cyber2000_crtcw(0x13, crtc[13]);
+#endif
 
-	cyber2000_outb(0x13, 0x3d4);
-	cyber2000_outb(scr_pitch, 0x3d5);
-	cyber2000_outb(0x14, 0x3ce);
-	cyber2000_outb(fetchrow, 0x3cf);
-	cyber2000_outb(0x15, 0x3ce);
+	cyber2000_grphw(0x14, fetchrow);
 					/* 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_outb(0x33, 0x3ce);
-	cyber2000_outb(0x1c, 0x3cf);
-
-	cyber2000_outw(width - 1, 0xbf018);
-	cyber2000_outw(width - 1, 0xbf218);
-	cyber2000_outb(b, 0xbf01c);
-
-	return 0;
+	cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f));
+	cyber2000_grphw(0x77, graph_r77);
+	cyber2000_grphw(0x33, 0x1c);
+
+	cyber2000_outw(width, 0xbf018);
+	cyber2000_outw(width, 0xbf218);
+	cyber2000_outb(b,     0xbf01c);
+
+{ int j;
+ printk(KERN_DEBUG);
+ for (j = 0; j < 19; j++) printk("%2d ", j); printk("\n"KERN_DEBUG);
+ for (j = 0; j < 19; j++) printk("%02X ", crtc[j]); printk("\n"KERN_DEBUG);
+ for (j = 0; j < 18; j++) printk("%02X ", cyber2000_res[i].crtc_regs[j]);
+ printk("%02X\n", cyber2000_res[i].crtc_ofl);
+}
 }
 
 static inline void
@@ -536,13 +648,10 @@
 
 	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);
+	cyber2000_crtcw(0x0c, base);
+	cyber2000_crtcw(0x0d, base >> 8);
 	/* FIXME: need the upper bits of the start offset */
-/*	cyber2000_outb(0x??, 0x3d4);
-	cyber2000_outb(base >> 16, 0x3d5);*/
+/*	cyber2000_crtcw(0x??, base >> 16);*/
 #endif
 }
 
@@ -622,6 +731,7 @@
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
+	case 15:
 	case 16:
 		*visual = FB_VISUAL_DIRECTCOLOR;
 		break;
@@ -737,6 +847,10 @@
 	}
 
 	display->var = *var;
+	display->var.activate &= ~FB_ACTIVATE_ALL;
+
+	if (var->activate & FB_ACTIVATE_ALL)
+		global_disp.var = display->var;
 
 	display->screen_base	= (char *)current_par.screen_base;
 	display->visual		= visual;
@@ -744,8 +858,6 @@
 	display->type_aux	= 0;
 	display->ypanstep	= 0;
 	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,18 +866,22 @@
 	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:
@@ -775,6 +891,8 @@
 		break;
 	}
 
+	display->line_length = display->next_line;
+
 	if (display->var.accel_flags & FB_ACCELF_TEXT &&
 	    dispsw != &fbcon_dummy)
 		display->dispsw = &fbcon_cyber_accel;
@@ -818,6 +936,7 @@
 		return -EINVAL;
 	if (y_bottom > fb_display[con].var.yres_virtual)
 		return -EINVAL;
+/*disabled until we can update the start address properly */
 return -EINVAL;
 
 	cyber2000fb_update_start(var);
@@ -947,12 +1066,26 @@
 	init_var.yres			= DEFAULT_YRES;
 	init_var.bits_per_pixel		= DEFAULT_BPP;
 
+	/*
+	 * These parameters give
+	 * 640x480, hsync 31.5kHz, vsync 60Hz
+	 */
+	init_var.left_margin		= 56;
+	init_var.right_margin		= 16;
+	init_var.upper_margin		= 34;
+	init_var.lower_margin		= 9;
+	init_var.hsync_len		= 88;
+	init_var.vsync_len		= 2;
+	init_var.pixclock		= 39722;
+
 	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 +1095,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 +1115,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;
diff -u --recursive --new-file v2.3.6/linux/drivers/video/cyber2000fb.h linux/drivers/video/cyber2000fb.h
--- v2.3.6/linux/drivers/video/cyber2000fb.h	Tue May 11 16:30:45 1999
+++ linux/drivers/video/cyber2000fb.h	Thu Jun 17 01:11:35 1999
@@ -13,19 +13,19 @@
 #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)
+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,7 +33,7 @@
 	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);
diff -u --recursive --new-file v2.3.6/linux/drivers/video/vgacon.c linux/drivers/video/vgacon.c
--- v2.3.6/linux/drivers/video/vgacon.c	Tue May 11 16:30:45 1999
+++ linux/drivers/video/vgacon.c	Wed Jun 16 19:26:27 1999
@@ -135,9 +135,17 @@
  */
 static inline void write_vga(unsigned char reg, unsigned int val)
 {
-#ifndef SLOW_VGA
 	unsigned int v1, v2;
+	unsigned long flags;
+
+	/*
+	 * ddprintk might set the console position from interrupt
+	 * handlers, thus the write has to be IRQ-atomic.
+	 */
+	save_flags(flags);
+	cli();
 
+#ifndef SLOW_VGA
 	v1 = reg + (val & 0xff00);
 	v2 = reg + 1 + ((val << 8) & 0xff00);
 	outw(v1, vga_video_port_reg);
@@ -148,6 +156,7 @@
 	outb_p(reg+1, vga_video_port_reg);
 	outb_p(val & 0xff, vga_video_port_val);
 #endif
+	restore_flags(flags);
 }
 
 __initfunc(static const char *vgacon_startup(void))
diff -u --recursive --new-file v2.3.6/linux/fs/Config.in linux/fs/Config.in
--- v2.3.6/linux/fs/Config.in	Wed Jun  9 18:42:08 1999
+++ linux/fs/Config.in	Wed Jun 16 19:26:27 1999
@@ -90,9 +90,6 @@
     fi
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS
-  if [ "$CONFIG_SMB_FS" != "n" ]; then
-    bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
-  fi
 fi
 if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
diff -u --recursive --new-file v2.3.6/linux/fs/affs/dir.c linux/fs/affs/dir.c
--- v2.3.6/linux/fs/affs/dir.c	Fri Apr 23 21:20:37 1999
+++ linux/fs/affs/dir.c	Sat Jun 19 11:45:28 1999
@@ -63,7 +63,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permissions */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.3.6/linux/fs/affs/file.c	Mon Aug 24 13:02:44 1998
+++ linux/fs/affs/file.c	Sat Jun 19 11:45:28 1999
@@ -80,7 +80,6 @@
 	affs_truncate,		/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
@@ -121,7 +120,6 @@
 	affs_truncate,		/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/autofs/dir.c linux/fs/autofs/dir.c
--- v2.3.6/linux/fs/autofs/dir.c	Thu May 13 10:53:59 1999
+++ linux/fs/autofs/dir.c	Sat Jun 19 11:45:28 1999
@@ -79,7 +79,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- v2.3.6/linux/fs/autofs/root.c	Thu May 13 10:53:59 1999
+++ linux/fs/autofs/root.c	Sat Jun 19 11:45:29 1999
@@ -60,7 +60,6 @@
         NULL,                   /* truncate */
         NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- v2.3.6/linux/fs/autofs/symlink.c	Tue Oct 27 14:13:53 1998
+++ linux/fs/autofs/symlink.c	Sat Jun 19 11:45:29 1999
@@ -55,6 +55,5 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/bad_inode.c linux/fs/bad_inode.c
--- v2.3.6/linux/fs/bad_inode.c	Mon Sep 21 14:37:20 1998
+++ linux/fs/bad_inode.c	Sat Jun 19 11:45:28 1999
@@ -60,13 +60,13 @@
 	EIO_ERROR,		/* rename */
 	EIO_ERROR,		/* readlink */
 	bad_follow_link,	/* follow_link */
+	EIO_ERROR,		/* bmap */
 	EIO_ERROR,		/* readpage */
 	EIO_ERROR,		/* writepage */
-	EIO_ERROR,		/* bmap */
+	EIO_ERROR,		/* flushpage */
 	EIO_ERROR,		/* truncate */
 	EIO_ERROR,		/* permission */
 	EIO_ERROR,		/* smap */
-	EIO_ERROR,		/* update_page */
 	EIO_ERROR		/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.3.6/linux/fs/binfmt_aout.c	Tue Jun  8 23:01:35 1999
+++ linux/fs/binfmt_aout.c	Fri Jun 18 08:00:52 1999
@@ -59,11 +59,7 @@
 
 static int dump_write(struct file *file, const void *addr, int nr)
 {
-	int r;
-	down(&file->f_dentry->d_inode->i_sem);
-	r = file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-	up(&file->f_dentry->d_inode->i_sem);
-	return r;
+	return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
 }
 
 #define DUMP_WRITE(addr, nr)	\
diff -u --recursive --new-file v2.3.6/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.6/linux/fs/binfmt_elf.c	Thu Jun  3 23:15:29 1999
+++ linux/fs/binfmt_elf.c	Fri Jun 18 08:01:10 1999
@@ -918,11 +918,7 @@
  */
 static int dump_write(struct file *file, const void *addr, int nr)
 {
-	int r;
-	down(&file->f_dentry->d_inode->i_sem);
-	r = file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-	up(&file->f_dentry->d_inode->i_sem);
-	return r;
+	return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
 }
 
 static int dump_seek(struct file *file, off_t off)
diff -u --recursive --new-file v2.3.6/linux/fs/block_dev.c linux/fs/block_dev.c
--- v2.3.6/linux/fs/block_dev.c	Fri May 28 09:20:31 1999
+++ linux/fs/block_dev.c	Sun Jun 20 01:09:22 1999
@@ -124,6 +124,7 @@
 			}
 			buffercount=0;
 		}
+		balance_dirty(dev);
 		if(write_error)
 			break;
 	}
diff -u --recursive --new-file v2.3.6/linux/fs/buffer.c linux/fs/buffer.c
--- v2.3.6/linux/fs/buffer.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/buffer.c	Sun Jun 20 15:58:20 1999
@@ -24,6 +24,8 @@
  * - RMK
  */
 
+#include <linux/sched.h>
+#include <linux/fs.h>
 #include <linux/malloc.h>
 #include <linux/locks.h>
 #include <linux/errno.h>
@@ -113,7 +115,7 @@
 
 /* These are the min and max parameter values that we will allow to be assigned */
 int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   1*HZ,   1*HZ, 1, 1};
-int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5};
+int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,1000, 6000*HZ, 6000*HZ, 2047, 5};
 
 void wakeup_bdflush(int);
 
@@ -422,7 +424,25 @@
 #define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask)
 #define hash(dev,block) hash_table[_hashfn(dev,block)]
 
-static inline void remove_from_hash_queue(struct buffer_head * bh)
+static void insert_into_hash_list(struct buffer_head * bh)
+{
+	bh->b_next = NULL;
+	bh->b_pprev = NULL;
+	if (bh->b_dev) {
+		struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr);
+		struct buffer_head *next = *bhp;
+
+		if (next) {
+			bh->b_next = next;
+			next->b_pprev = &bh->b_next;
+		}
+		*bhp = bh;
+		bh->b_pprev = bhp;
+		nr_hashed_buffers++;
+	}
+}
+
+static void remove_from_hash_queue(struct buffer_head * bh)
 {
 	struct buffer_head **pprev = bh->b_pprev;
 	if (pprev) {
@@ -433,16 +453,43 @@
 		}
 		*pprev = next;
 		bh->b_pprev = NULL;
+		nr_hashed_buffers--;
 	}
-	nr_hashed_buffers--;
 }
 
-static inline void remove_from_lru_list(struct buffer_head * bh)
+static void insert_into_lru_list(struct buffer_head * bh)
 {
-	if (!(bh->b_prev_free) || !(bh->b_next_free))
-		panic("VFS: LRU block list corrupted");
+	struct buffer_head **bhp = &lru_list[bh->b_list];
+
 	if (bh->b_dev == B_FREE)
-		panic("LRU list corrupted");
+		BUG();
+
+	if(!*bhp) {
+		*bhp = bh;
+		bh->b_prev_free = bh;
+	}
+
+	if (bh->b_next_free)
+		panic("VFS: buffer LRU pointers corrupted");
+
+	bh->b_next_free = *bhp;
+	bh->b_prev_free = (*bhp)->b_prev_free;
+	(*bhp)->b_prev_free->b_next_free = bh;
+	(*bhp)->b_prev_free = bh;
+
+	nr_buffers++;
+	nr_buffers_type[bh->b_list]++;
+}
+
+static void remove_from_lru_list(struct buffer_head * bh)
+{
+	if (!(bh->b_prev_free) || !(bh->b_next_free))
+		return;
+
+	if (bh->b_dev == B_FREE) {
+		printk("LRU list corrupted");
+		*(int*)0 = 0;
+	}
 	bh->b_prev_free->b_next_free = bh->b_next_free;
 	bh->b_next_free->b_prev_free = bh->b_prev_free;
 
@@ -451,9 +498,12 @@
 	if (lru_list[bh->b_list] == bh)
 		 lru_list[bh->b_list] = NULL;
 	bh->b_next_free = bh->b_prev_free = NULL;
+
+	nr_buffers--;
+	nr_buffers_type[bh->b_list]--;
 }
 
-static inline void remove_from_free_list(struct buffer_head * bh)
+static void remove_from_free_list(struct buffer_head * bh)
 {
 	int isize = BUFSIZE_INDEX(bh->b_size);
 	if (!(bh->b_prev_free) || !(bh->b_next_free))
@@ -475,21 +525,20 @@
 
 static void remove_from_queues(struct buffer_head * bh)
 {
-	if(bh->b_dev == B_FREE) {
-		remove_from_free_list(bh); /* Free list entries should not be
-					      in the hash queue */
-		return;
-	}
-	nr_buffers_type[bh->b_list]--;
+	if (bh->b_dev == B_FREE)
+		BUG();
 	remove_from_hash_queue(bh);
 	remove_from_lru_list(bh);
 }
 
-static inline void put_last_free(struct buffer_head * bh)
+static void put_last_free(struct buffer_head * bh)
 {
 	if (bh) {
 		struct buffer_head **bhp = &free_list[BUFSIZE_INDEX(bh->b_size)];
 
+		if (bh->b_count)
+			BUG();
+
 		bh->b_dev = B_FREE;  /* So it is obvious we are on the free list. */
 
 		/* Add to back of free list. */
@@ -505,47 +554,6 @@
 	}
 }
 
-static void insert_into_queues(struct buffer_head * bh)
-{
-	/* put at end of free list */
-	if(bh->b_dev == B_FREE) {
-		put_last_free(bh);
-	} else {
-		struct buffer_head **bhp = &lru_list[bh->b_list];
-
-		if(!*bhp) {
-			*bhp = bh;
-			bh->b_prev_free = bh;
-		}
-
-		if (bh->b_next_free)
-			panic("VFS: buffer LRU pointers corrupted");
-
-		bh->b_next_free = *bhp;
-		bh->b_prev_free = (*bhp)->b_prev_free;
-		(*bhp)->b_prev_free->b_next_free = bh;
-		(*bhp)->b_prev_free = bh;
-
-		nr_buffers_type[bh->b_list]++;
-
-		/* Put the buffer in new hash-queue if it has a device. */
-		bh->b_next = NULL;
-		bh->b_pprev = NULL;
-		if (bh->b_dev) {
-			struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr);
-			struct buffer_head *next = *bhp;
-
-			if (next) {
-				bh->b_next = next;
-				next->b_pprev = &bh->b_next;
-			}
-			*bhp = bh;
-			bh->b_pprev = bhp;
-		}
-		nr_hashed_buffers++;
-	}
-}
-
 struct buffer_head * find_buffer(kdev_t dev, int block, int size)
 {		
 	struct buffer_head * next;
@@ -636,6 +644,7 @@
 			if (bh->b_size == size)
 				 continue;
 			bhnext->b_count++;
+			bh->b_count++;
 			wait_on_buffer(bh);
 			bhnext->b_count--;
 			if (bh->b_dev == dev && bh->b_size != size) {
@@ -644,9 +653,10 @@
 				clear_bit(BH_Req, &bh->b_state);
 				bh->b_flushtime = 0;
 			}
+			if (--bh->b_count)
+				continue;
 			remove_from_queues(bh);
-			bh->b_dev=B_FREE;
-			insert_into_queues(bh);
+			put_last_free(bh);
 		}
 	}
 }
@@ -666,7 +676,6 @@
 void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
 		 bh_end_io_t *handler, void *dev_id)
 {
-	bh->b_count = 1;
 	bh->b_list = BUF_CLEAN;
 	bh->b_flushtime = 0;
 	bh->b_dev = dev;
@@ -702,7 +711,7 @@
 		if (!buffer_dirty(bh)) {
 			bh->b_flushtime = 0;
 		}
-		return bh;
+		goto out;
 	}
 
 	isize = BUFSIZE_INDEX(size);
@@ -716,9 +725,13 @@
 	 * and that it's unused (b_count=0), unlocked, and clean.
 	 */
 	init_buffer(bh, dev, block, end_buffer_io_sync, NULL);
-	bh->b_state=0;
-	insert_into_queues(bh);
-	return bh;
+	bh->b_count = 1;
+	bh->b_state = 0;
+
+	/* Insert the buffer into the regular lists */
+	insert_into_lru_list(bh);
+	insert_into_hash_list(bh);
+	goto out;
 
 	/*
 	 * If we block while refilling the free list, somebody may
@@ -729,6 +742,8 @@
 	if (!find_buffer(dev,block,size))
 		goto get_free;
 	goto repeat;
+out:
+	return bh;
 }
 
 void set_writetime(struct buffer_head * buf, int flag)
@@ -746,15 +761,56 @@
 	}
 }
 
-
 /*
  * Put a buffer into the appropriate list, without side-effects.
  */
-static inline void file_buffer(struct buffer_head *bh, int list)
+static void file_buffer(struct buffer_head *bh, int list)
 {
-	remove_from_queues(bh);
+	remove_from_lru_list(bh);
 	bh->b_list = list;
-	insert_into_queues(bh);
+	insert_into_lru_list(bh);
+}
+
+/*
+ * if a new dirty buffer is created we need to balance bdflush.
+ *
+ * in the future we might want to make bdflush aware of different
+ * pressures on different devices - thus the (currently unused)
+ * 'dev' parameter.
+ */
+void balance_dirty(kdev_t dev)
+{
+	int dirty = nr_buffers_type[BUF_DIRTY];
+	int ndirty = bdf_prm.b_un.ndirty;
+
+	if (dirty > ndirty) {
+		int wait = 0;
+		if (dirty > 2*ndirty)
+			wait = 1;
+		wakeup_bdflush(wait);
+	}
+}
+
+atomic_t too_many_dirty_buffers;
+
+static inline void __mark_dirty(struct buffer_head *bh, int flag)
+{
+	set_writetime(bh, flag);
+	refile_buffer(bh);
+	if (atomic_read(&too_many_dirty_buffers))
+		balance_dirty(bh->b_dev);
+}
+
+void __mark_buffer_dirty(struct buffer_head *bh, int flag)
+{
+	__mark_dirty(bh, flag);
+}
+
+void __atomic_mark_buffer_dirty(struct buffer_head *bh, int flag)
+{
+	lock_kernel();
+	__mark_dirty(bh, flag);
+	unlock_kernel();
 }
 
 /*
@@ -765,36 +821,19 @@
 {
 	int dispose;
 
-	if(buf->b_dev == B_FREE) {
+	if (buf->b_dev == B_FREE) {
 		printk("Attempt to refile free buffer\n");
 		return;
 	}
+
+	dispose = BUF_CLEAN;
+	if (buffer_locked(buf))
+		dispose = BUF_LOCKED;
 	if (buffer_dirty(buf))
 		dispose = BUF_DIRTY;
-	else if (buffer_locked(buf))
-		dispose = BUF_LOCKED;
-	else
-		dispose = BUF_CLEAN;
-	if(dispose != buf->b_list) {
-		file_buffer(buf, dispose);
-		if(dispose == BUF_DIRTY) {
-			int too_many = (nr_buffers * bdf_prm.b_un.nfract/100);
-
-			/* This buffer is dirty, maybe we need to start flushing.
-			 * If too high a percentage of the buffers are dirty...
-			 */
-			if (nr_buffers_type[BUF_DIRTY] > too_many)
-				wakeup_bdflush(1);
 
-			/* If this is a loop device, and
-			 * more than half of the buffers are dirty...
-			 * (Prevents no-free-buffers deadlock with loop device.)
-			 */
-			if (MAJOR(buf->b_dev) == LOOP_MAJOR &&
-			    nr_buffers_type[BUF_DIRTY]*2>nr_buffers)
-				wakeup_bdflush(1);
-		}
-	}
+	if (dispose != buf->b_list)
+		file_buffer(buf, dispose);
 }
 
 /*
@@ -809,6 +848,7 @@
 
 	if (buf->b_count) {
 		buf->b_count--;
+		wake_up(&buffer_wait);
 		return;
 	}
 	printk("VFS: brelse: Trying to free free buffer\n");
@@ -890,7 +930,6 @@
 
 /*	if (blocks) printk("breada (new) %d blocks\n",blocks); */
 
-
 	bhlist[0] = bh;
 	j = 1;
 	for(i=1; i<blocks; i++) {
@@ -928,7 +967,8 @@
 		return;
 	}
 
-	memset(bh,0,sizeof(*bh));
+//	memset(bh, 0, sizeof(*bh));
+	bh->b_blocknr = -1;
 	init_waitqueue_head(&bh->b_wait);
 	nr_unused_buffer_heads++;
 	bh->b_next_free = unused_list;
@@ -1153,17 +1193,12 @@
 	struct page *page;
 
 	mark_buffer_uptodate(bh, uptodate);
-	unlock_buffer(bh);
 
 	/* This is a temporary buffer used for page I/O. */
 	page = mem_map + MAP_NR(bh->b_data);
-	if (!PageLocked(page))
-		goto not_locked;
-	if (bh->b_count != 1)
-		goto bad_count;
 
-	if (!test_bit(BH_Uptodate, &bh->b_state))
-		set_bit(PG_error, &page->flags);
+	if (!uptodate)
+		SetPageError(page);
 
 	/*
 	 * Be _very_ careful from here on. Bad things can happen if
@@ -1179,69 +1214,63 @@
 	 */
 	save_flags(flags);
 	cli();
-	bh->b_count--;
-	tmp = bh;
-	do {
-		if (tmp->b_count)
+	unlock_buffer(bh);
+	tmp = bh->b_this_page;
+	while (tmp != bh) {
+		if (buffer_locked(tmp))
 			goto still_busy;
 		tmp = tmp->b_this_page;
-	} while (tmp != bh);
+	}
 
 	/* OK, the async IO on this page is complete. */
-	free_async_buffers(bh);
 	restore_flags(flags);
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+
 	after_unlock_page(page);
+	/*
+	 * if none of the buffers had errors then we can set the
+	 * page uptodate:
+	 */
+	if (!PageError(page))
+		SetPageUptodate(page);
+	if (page->owner != -1)
+		PAGE_BUG(page);
+	page->owner = (int)current;
+	UnlockPage(page);
+
 	return;
 
 still_busy:
 	restore_flags(flags);
 	return;
-
-not_locked:
-	printk ("Whoops: end_buffer_io_async: async io complete on unlocked page\n");
-	return;
-
-bad_count:
-	printk ("Whoops: end_buffer_io_async: b_count != 1 on async io.\n");
-	return;
 }
 
-/*
- * Start I/O on a page.
- * This function expects the page to be locked and may return before I/O is complete.
- * You then have to check page->locked, page->uptodate, and maybe wait on page->wait.
- */
-int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
+static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
 {
-	struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
-	int block, nr;
+	struct buffer_head *head, *bh, *tail;
+	int block;
 
 	if (!PageLocked(page))
-		panic("brw_page: page not locked for I/O");
-	clear_bit(PG_uptodate, &page->flags);
-	clear_bit(PG_error, &page->flags);
+		BUG();
+	if (page->owner != (int)current)
+		PAGE_BUG(page);
 	/*
 	 * Allocate async buffer heads pointing to this page, just for I/O.
-	 * They do _not_ show up in the buffer hash table!
-	 * They are _not_ registered in page->buffers either!
+	 * They show up in the buffer hash table and are registered in
+	 * page->buffers.
 	 */
-	bh = create_buffers(page_address(page), size, 1);
-	if (!bh) {
-		/* WSH: exit here leaves page->count incremented */
-		clear_bit(PG_locked, &page->flags);
-		wake_up(&page->wait);
-		return -ENOMEM;
-	}
-	nr = 0;
-	next = bh;
-	do {
-		struct buffer_head * tmp;
+	lock_kernel();
+	head = create_buffers(page_address(page), size, 1);
+	unlock_kernel();
+	if (page->buffers)
+		BUG();
+	if (!head)
+		BUG();
+	tail = head;
+	for (bh = head; bh; bh = bh->b_this_page) {
 		block = *(b++);
 
-		init_buffer(next, dev, block, end_buffer_io_async, NULL);
-		set_bit(BH_Uptodate, &next->b_state);
+		tail = bh;
+		init_buffer(bh, dev, block, end_buffer_io_async, NULL);
 
 		/*
 		 * When we use bmap, we define block zero to represent
@@ -1250,51 +1279,379 @@
 		 * two cases.
 		 */
 		if (bmap && !block) {
-			memset(next->b_data, 0, size);
-			next->b_count--;
-			continue;
+			set_bit(BH_Uptodate, &bh->b_state);
+			memset(bh->b_data, 0, size);
+		}
+	}
+	tail->b_this_page = head;
+	get_page(page);
+	page->buffers = head;
+	return 0;
+}
+
+/*
+ * We don't have to release all buffers here, but
+ * we have to be sure that no dirty buffer is left
+ * and no IO is going on (no buffer is locked), because
+ * we have truncated the file and are going to free the
+ * blocks on-disk..
+ */
+int block_flushpage(struct inode *inode, struct page *page, unsigned long offset)
+{
+	struct buffer_head *head, *bh, *next;
+	unsigned int curr_off = 0;
+
+	if (!PageLocked(page))
+		BUG();
+	if (!page->buffers)
+		return 0;
+	lock_kernel();
+
+	head = page->buffers;
+	bh = head;
+	do {
+		unsigned int next_off = curr_off + bh->b_size;
+		next = bh->b_this_page;
+
+		/*
+		 * is this block fully flushed?
+		 */
+		if (offset <= curr_off) {
+			if (bh->b_blocknr) {
+				bh->b_count++;
+				wait_on_buffer(bh);
+				if (bh->b_dev == B_FREE)
+					BUG();
+				mark_buffer_clean(bh);
+				bh->b_blocknr = 0;
+				bh->b_count--;
+			}
+		}
+		curr_off = next_off;
+		bh = next;
+	} while (bh != head);
+
+	/*
+	 * subtle. We release buffer-heads only if this is
+	 * the 'final' flushpage. We have invalidated the bmap
+	 * cached value unconditionally, so real IO is not
+	 * possible anymore.
+	 */
+	if (!offset)
+		try_to_free_buffers(page);
+
+	unlock_kernel();
+	return 0;
+}
+
+static void create_empty_buffers (struct page *page,
+			struct inode *inode, unsigned long blocksize)
+{
+	struct buffer_head *bh, *head, *tail;
+
+	lock_kernel();
+	head = create_buffers(page_address(page), blocksize, 1);
+	unlock_kernel();
+	if (page->buffers)
+		BUG();
+
+	bh = head;
+	do {
+		bh->b_dev = inode->i_dev;
+		bh->b_blocknr = 0;
+		tail = bh;
+		bh = bh->b_this_page;
+	} while (bh);
+	tail->b_this_page = head;
+	page->buffers = head;
+	get_page(page);
+}
+
+/*
+ * block_write_full_page() is SMP-safe - currently it's still
+ * being called with the kernel lock held, but the code is ready.
+ */
+int block_write_full_page (struct file *file, struct page *page, fs_getblock_t fs_get_block)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	int err, created, i;
+	unsigned long block, phys, offset;
+	struct buffer_head *bh, *head;
+
+	if (!PageLocked(page))
+		BUG();
+
+	if (!page->buffers)
+		create_empty_buffers(page, inode, inode->i_sb->s_blocksize);
+	head = page->buffers;
+
+	offset = page->offset;
+	block = offset >> inode->i_sb->s_blocksize_bits;
+
+	// FIXME: currently we assume page alignment.
+	if (offset & (PAGE_SIZE-1))
+		BUG();
+
+	bh = head;
+	i = 0;
+	do {
+		if (!bh)
+			BUG();
+
+		if (!bh->b_blocknr) {
+			err = -EIO;
+			down(&inode->i_sem);
+			phys = fs_get_block (inode, block, 1, &err, &created);
+			up(&inode->i_sem);
+			if (!phys)
+				goto out;
+
+			init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL);
+			bh->b_state = (1<<BH_Uptodate);
+		} else {
+			/*
+			 * block already exists, just mark it uptodate and
+			 * dirty:
+			 */
+			bh->b_end_io = end_buffer_io_sync;
+			set_bit(BH_Uptodate, &bh->b_state);
 		}
-		tmp = get_hash_table(dev, block, size);
-		if (tmp) {
-			if (!buffer_uptodate(tmp)) {
-				if (rw == READ)
-					ll_rw_block(READ, 1, &tmp);
-				wait_on_buffer(tmp);
+		atomic_mark_buffer_dirty(bh,0);
+
+		bh = bh->b_this_page;
+		block++;
+	} while (bh != head);
+
+	SetPageUptodate(page);
+	return 0;
+out:
+	ClearPageUptodate(page);
+	return err;
+}
+
+int block_write_partial_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	unsigned long block;
+	int err, created, partial;
+	unsigned long blocksize, start_block, end_block;
+	unsigned long start_offset, start_bytes, end_bytes;
+	unsigned long bbits, phys, blocks, i, len;
+	struct buffer_head *bh, *head;
+	char * target_buf;
+
+	target_buf = (char *)page_address(page) + offset;
+
+	if (!PageLocked(page))
+		BUG();
+
+	blocksize = inode->i_sb->s_blocksize;
+	if (!page->buffers)
+		create_empty_buffers(page, inode, blocksize);
+	head = page->buffers;
+
+	bbits = inode->i_sb->s_blocksize_bits;
+	block = page->offset >> bbits;
+	blocks = PAGE_SIZE >> bbits;
+	start_block = offset >> bbits;
+	end_block = (offset + bytes - 1) >> bbits;
+	start_offset = offset & (blocksize - 1);
+	start_bytes = blocksize - start_offset;
+	if (start_bytes > bytes)
+		start_bytes = bytes;
+	end_bytes = (offset+bytes) & (blocksize - 1);
+	if (end_bytes > bytes)
+		end_bytes = bytes;
+
+	if (offset < 0 || offset >= PAGE_SIZE)
+		BUG();
+	if (bytes+offset < 0 || bytes+offset > PAGE_SIZE)
+		BUG();
+	if (start_block < 0 || start_block >= blocks)
+		BUG();
+	if (end_block < 0 || end_block >= blocks)
+		BUG();
+	// FIXME: currently we assume page alignment.
+	if (page->offset & (PAGE_SIZE-1))
+		BUG();
+
+	i = 0;
+	bh = head;
+	partial = 0;
+	do {
+		if (!bh)
+			BUG();
+
+		if ((i < start_block) || (i > end_block)) {
+			if (!buffer_uptodate(bh))
+				partial = 1;
+			goto skip;
+		}
+		if (!bh->b_blocknr) {
+			err = -EIO;
+			down(&inode->i_sem);
+			phys = fs_get_block (inode, block, 1, &err, &created);
+			up(&inode->i_sem);
+			if (!phys)
+				goto out;
+
+			init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL);
+
+			/*
+			 * if partially written block which has contents on
+			 * disk, then we have to read it first.
+			 * We also rely on the fact that filesystem holes
+			 * cannot be written.
+			 */
+			if (!created && (start_offset ||
+					(end_bytes && (i == end_block)))) {
+				bh->b_state = 0;
+				ll_rw_block(READ, 1, &bh);
+				lock_kernel();
+				wait_on_buffer(bh);
+				unlock_kernel();
+				err = -EIO;
+				if (!buffer_uptodate(bh))
+					goto out;
 			}
-			if (rw == READ) 
-				memcpy(next->b_data, tmp->b_data, size);
-			else {
-				memcpy(tmp->b_data, next->b_data, size);
-				mark_buffer_dirty(tmp, 0);
+
+			bh->b_state = (1<<BH_Uptodate);
+		} else {
+			/*
+			 * block already exists, just mark it uptodate:
+			 */
+			bh->b_end_io = end_buffer_io_sync;
+			set_bit(BH_Uptodate, &bh->b_state);
+		}
+
+		err = -EFAULT;
+		if (start_offset) {
+			len = start_bytes;
+			start_offset = 0;
+		} else
+		if (end_bytes && (i == end_block)) {
+			len = end_bytes;
+			end_bytes = 0;
+		} else {
+			/*
+			 * Overwritten block.
+			 */
+			len = blocksize;
+		}
+		if (copy_from_user(target_buf, buf, len))
+			goto out;
+		target_buf += len;
+		buf += len;
+
+		/*
+		 * we dirty buffers only after copying the data into
+		 * the page - this way we can dirty the buffer even if
+		 * the bh is still doing IO.
+		 */
+		atomic_mark_buffer_dirty(bh,0);
+skip:
+		i++;
+		block++;
+		bh = bh->b_this_page;
+	} while (bh != head);
+
+	/*
+	 * is this a partial write that happened to make all buffers
+	 * uptodate then we can optimize away a bogus readpage() for
+	 * the next read(). Here we 'discover' wether the page went
+	 * uptodate as a result of this (potentially partial) write.
+	 */
+	if (!partial)
+		SetPageUptodate(page);
+	return bytes;
+out:
+	ClearPageUptodate(page);
+	return err;
+}
+
+/*
+ * Start I/O on a page.
+ * This function expects the page to be locked and may return
+ * before I/O is complete. You then have to check page->locked,
+ * page->uptodate, and maybe wait on page->wait.
+ *
+ * brw_page() is SMP-safe, although it's being called with the
+ * kernel lock held - but the code is ready.
+ */
+int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
+{
+	struct buffer_head *head, *bh, *arr[MAX_BUF_PER_PAGE];
+	int nr, fresh /* temporary debugging flag */, block;
+
+	if (!PageLocked(page))
+		panic("brw_page: page not locked for I/O");
+//	clear_bit(PG_error, &page->flags);
+	/*
+	 * We pretty much rely on the page lock for this, because
+	 * create_page_buffers() might sleep.
+	 */
+	fresh = 0;
+	if (!page->buffers) {
+		create_page_buffers(rw, page, dev, b, size, bmap);
+		fresh = 1;
+	}
+	if (!page->buffers)
+		BUG();
+	page->owner = -1;
+
+	head = page->buffers;
+	bh = head;
+	nr = 0;
+	do {
+		block = *(b++);
+
+		if (fresh && (bh->b_count != 0))
+			BUG();
+		if (rw == READ) {
+			if (!fresh)
+				BUG();
+			if (bmap && !block) {
+				if (block)
+					BUG();
+			} else {
+				if (bmap && !block)
+					BUG();
+				if (!buffer_uptodate(bh)) {
+					arr[nr++] = bh;
+				}
 			}
-			brelse(tmp);
-			next->b_count--;
-			continue;
+		} else { /* WRITE */
+			if (!bh->b_blocknr) {
+				if (!block)
+					BUG();
+				bh->b_blocknr = block;
+			} else {
+				if (!block)
+					BUG();
+			}
+			set_bit(BH_Uptodate, &bh->b_state);
+			atomic_mark_buffer_dirty(bh, 0);
+			arr[nr++] = bh;
 		}
-		if (rw == READ)
-			clear_bit(BH_Uptodate, &next->b_state);
-		else
-			set_bit(BH_Dirty, &next->b_state);
-		arr[nr++] = next;
-	} while (prev = next, (next = next->b_this_page) != NULL);
-	prev->b_this_page = bh;
-	
-	if (nr) {
+		bh = bh->b_this_page;
+	} while (bh != head);
+	if (rw == READ)
+		++current->maj_flt;
+	if ((rw == READ) && nr) {
+		if (Page_Uptodate(page))
+			BUG();
 		ll_rw_block(rw, nr, arr);
-		/* The rest of the work is done in mark_buffer_uptodate()
-		 * and unlock_buffer(). */
 	} else {
-		unsigned long flags;
-		clear_bit(PG_locked, &page->flags);
-		set_bit(PG_uptodate, &page->flags);
-		wake_up(&page->wait);
-		save_flags(flags);
-		cli();
-		free_async_buffers(bh);
-		restore_flags(flags);
-		after_unlock_page(page);
+		if (!nr && rw == READ) {
+			SetPageUptodate(page);
+			page->owner = (int)current;
+			UnlockPage(page);
+		}
+		if (nr && (rw == WRITE))
+			ll_rw_block(rw, nr, arr);
 	}
-	++current->maj_flt;
 	return 0;
 }
 
@@ -1305,6 +1662,7 @@
 {
 	if (on) {
 		struct buffer_head *tmp = bh;
+		struct page *page;
 		set_bit(BH_Uptodate, &bh->b_state);
 		/* If a page has buffers and all these buffers are uptodate,
 		 * then the page is uptodate. */
@@ -1313,7 +1671,8 @@
 				return;
 			tmp=tmp->b_this_page;
 		} while (tmp && tmp != bh);
-		set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags);
+		page = mem_map + MAP_NR(bh->b_data);
+		SetPageUptodate(page);
 		return;
 	}
 	clear_bit(BH_Uptodate, &bh->b_state);
@@ -1326,30 +1685,70 @@
  * mark_buffer_uptodate() functions propagate buffer state into the
  * page struct once IO has completed.
  */
-int generic_readpage(struct file * file, struct page * page)
+int block_read_full_page(struct file * file, struct page * page)
 {
 	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
-	unsigned long block;
-	int *p, nr[PAGE_SIZE/512];
-	int i;
+	unsigned long iblock, phys_block;
+	struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
+	unsigned int blocksize, blocks;
+	int nr;
 
-	atomic_inc(&page->count);
-	set_bit(PG_locked, &page->flags);
-	set_bit(PG_free_after, &page->flags);
-	
-	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
-	block = page->offset >> inode->i_sb->s_blocksize_bits;
-	p = nr;
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+	blocksize = inode->i_sb->s_blocksize;
+	if (!page->buffers)
+		create_empty_buffers(page, inode, blocksize);
+	head = page->buffers;
+
+	blocks = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+	iblock = page->offset >> inode->i_sb->s_blocksize_bits;
+	page->owner = -1;
+	head = page->buffers;
+	bh = head;
+	nr = 0;
 	do {
-		*p = inode->i_op->bmap(inode, block);
-		i--;
-		block++;
-		p++;
-	} while (i > 0);
+		phys_block = bh->b_blocknr;
+		/*
+		 * important, we have to retry buffers that already have
+		 * their bnr cached but had an IO error!
+		 */
+		if (!buffer_uptodate(bh)) {
+			phys_block = inode->i_op->bmap(inode, iblock);
+			/*
+			 * this is safe to do because we hold the page lock:
+			 */
+			if (phys_block) {
+				init_buffer(bh, inode->i_dev, phys_block,
+						end_buffer_io_async, NULL);
+				arr[nr] = bh;
+				nr++;
+			} else {
+				/*
+				 * filesystem 'hole' represents zero-contents:
+				 */
+				memset(bh->b_data, 0, blocksize);
+				set_bit(BH_Uptodate, &bh->b_state);
+			}
+		}
+		iblock++;
+		bh = bh->b_this_page;
+	} while (bh != head);
 
-	/* IO start */
-	brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
+	++current->maj_flt;
+	if (nr) {
+		if (Page_Uptodate(page))
+			BUG();
+		ll_rw_block(READ, nr, arr);
+	} else {
+		/*
+		 * all buffers are uptodate - we can set the page
+		 * uptodate as well.
+		 */
+		SetPageUptodate(page);
+		page->owner = (int)current;
+		UnlockPage(page);
+	}
 	return 0;
 }
 
@@ -1392,7 +1791,6 @@
 			tmp->b_next_free = tmp;
 		}
 		insert_point = tmp;
-		++nr_buffers;
 		if (tmp->b_this_page)
 			tmp = tmp->b_this_page;
 		else
@@ -1409,7 +1807,7 @@
  * Can the buffer be thrown out?
  */
 #define BUFFER_BUSY_BITS	((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected))
-#define buffer_busy(bh)		((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS))
+#define buffer_busy(bh)	((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS))
 
 /*
  * try_to_free_buffers() checks if all the buffers on this particular page
@@ -1418,9 +1816,9 @@
  * Wake up bdflush() if this fails - if we're running low on memory due
  * to dirty buffers, we need to flush them out as quickly as possible.
  */
-int try_to_free_buffers(struct page * page_map)
+int try_to_free_buffers(struct page * page)
 {
-	struct buffer_head * tmp, * bh = page_map->buffers;
+	struct buffer_head * tmp, * bh = page->buffers;
 
 	tmp = bh;
 	do {
@@ -1429,8 +1827,6 @@
 		tmp = tmp->b_this_page;
 		if (!buffer_busy(p))
 			continue;
-
-		wakeup_bdflush(0);
 		return 0;
 	} while (tmp != bh);
 
@@ -1438,8 +1834,13 @@
 	do {
 		struct buffer_head * p = tmp;
 		tmp = tmp->b_this_page;
-		nr_buffers--;
-		remove_from_queues(p);
+
+		/* The buffer can be either on the regular queues or on the free list.. */		
+		if (p->b_dev == B_FREE)
+			remove_from_free_list(p);
+		else
+			remove_from_queues(p);
+
 		put_unused_buffer_head(p);
 	} while (tmp != bh);
 
@@ -1447,10 +1848,12 @@
 	wake_up(&buffer_wait);
 
 	/* And free the page */
-	buffermem -= PAGE_SIZE;
-	page_map->buffers = NULL;
-	__free_page(page_map);
-	return 1;
+	page->buffers = NULL;
+	if (__free_page(page)) {
+		buffermem -= PAGE_SIZE;
+		return 1;
+	}
+	return 0;
 }
 
 /* ================== Debugging =================== */
@@ -1509,11 +1912,11 @@
 	   the heuristic from working with large databases and getting
 	   fsync times (ext2) manageable, is the following */
 
-	memory_size >>= 20;
+	memory_size >>= 22;
 	for (order = 5; (1UL << order) < memory_size; order++);
 
 	/* try to allocate something until we get it or we're asking
-           for something that is really too small */
+	   for something that is really too small */
 
 	do {
 		nr_hash = (1UL << order) * PAGE_SIZE /
@@ -1521,6 +1924,7 @@
 		hash_table = (struct buffer_head **)
 		    __get_free_pages(GFP_ATOMIC, order);
 	} while (hash_table == NULL && --order > 4);
+	printk("buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", nr_hash, order, (1UL<<order) * PAGE_SIZE);
 	
 	if (!hash_table)
 		panic("Failed to allocate buffer hash table\n");
@@ -1565,11 +1969,11 @@
 {
 	if (current == bdflush_tsk)
 		return;
-	wake_up(&bdflush_wait);
-	if (wait) {
+	if (wait)
 		run_task_queue(&tq_disk);
+	wake_up(&bdflush_wait);
+	if (wait)
 		sleep_on(&bdflush_done);
-	}
 }
 
 
@@ -1801,6 +2205,7 @@
 #endif
 					  bh->b_count--;
 					  next->b_count--;
+					  wake_up(&buffer_wait);
 				  }
 		 }
 #ifdef DEBUG
@@ -1818,9 +2223,14 @@
 		run_task_queue(&tq_disk);
 		wake_up(&bdflush_done);
 		
-		/* If there are still a lot of dirty buffers around, skip the sleep
-		   and flush some more */
-		if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+		/*
+		 * If there are still a lot of dirty buffers around,
+		 * skip the sleep and flush some more
+		 */
+		if ((ndirty == 0) || (nr_buffers_type[BUF_DIRTY] <=
+				 nr_buffers * bdf_prm.b_un.nfract/100)) {
+
+			atomic_set(&too_many_dirty_buffers, 0);
 			spin_lock_irq(&current->sigmask_lock);
 			flush_signals(current);
 			spin_unlock_irq(&current->sigmask_lock);
diff -u --recursive --new-file v2.3.6/linux/fs/devices.c linux/fs/devices.c
--- v2.3.6/linux/fs/devices.c	Sat May 15 23:43:04 1999
+++ linux/fs/devices.c	Sat Jun 19 11:45:28 1999
@@ -277,11 +277,14 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
diff -u --recursive --new-file v2.3.6/linux/fs/devpts/root.c linux/fs/devpts/root.c
--- v2.3.6/linux/fs/devpts/root.c	Sat May  8 17:56:37 1999
+++ linux/fs/devpts/root.c	Sat Jun 19 11:45:29 1999
@@ -57,7 +57,6 @@
 	NULL,                   /* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v2.3.6/linux/fs/ext2/balloc.c	Wed Oct 28 21:54:56 1998
+++ linux/fs/ext2/balloc.c	Wed Jun 16 19:26:27 1999
@@ -358,7 +358,7 @@
  * bitmap, and then for any free bit if that fails.
  */
 int ext2_new_block (const struct inode * inode, unsigned long goal,
-		    u32 * prealloc_count, u32 * prealloc_block, int * err)
+    u32 * prealloc_count, u32 * prealloc_block, int * err)
 {
 	struct buffer_head * bh;
 	struct buffer_head * bh2;
@@ -594,20 +594,12 @@
 
 	if (j >= le32_to_cpu(es->s_blocks_count)) {
 		ext2_error (sb, "ext2_new_block",
-			    "block >= blocks count - "
-			    "block_group = %d, block=%d", i, j);
+			    "block(%d) >= blocks count(%d) - "
+			    "block_group = %d, es == %p ",j,
+			le32_to_cpu(es->s_blocks_count), i, es);
 		unlock_super (sb);
 		return 0;
 	}
-	if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
-		ext2_error (sb, "ext2_new_block", "cannot get block %d", j);
-		unlock_super (sb);
-		return 0;
-	}
-	memset(bh->b_data, 0, sb->s_blocksize);
-	mark_buffer_uptodate(bh, 1);
-	mark_buffer_dirty(bh, 1);
-	brelse (bh);
 
 	ext2_debug ("allocating block %d. "
 		    "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/dir.c linux/fs/ext2/dir.c
--- v2.3.6/linux/fs/ext2/dir.c	Fri Apr 23 21:20:37 1999
+++ linux/fs/ext2/dir.c	Sat Jun 19 11:45:28 1999
@@ -67,12 +67,14 @@
 	ext2_rename,		/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
 	ext2_permission,	/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 int ext2_check_dir_entry (const char * function, struct inode * dir,
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v2.3.6/linux/fs/ext2/file.c	Mon Dec 21 15:22:54 1998
+++ linux/fs/ext2/file.c	Sun Jun 20 01:09:22 1999
@@ -30,15 +30,15 @@
 #include <linux/locks.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
+#include <linux/smp_lock.h>
 
 #define	NBUF	32
 
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
+static int ext2_writepage (struct file * file, struct page * page);
 static long long ext2_file_lseek(struct file *, long long, int);
-static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *);
-static int ext2_release_file (struct inode *, struct file *);
 #if BITS_PER_LONG < 64
 static int ext2_open_file (struct inode *, struct file *);
 
@@ -57,51 +57,6 @@
 
 #endif
 
-/*
- * We have mostly NULL's here: the current defaults are ok for
- * the ext2 filesystem.
- */
-static struct file_operations ext2_file_operations = {
-	ext2_file_lseek,	/* lseek */
-	generic_file_read,	/* read */
-	ext2_file_write,	/* write */
-	NULL,			/* readdir - bad */
-	NULL,			/* poll - default */
-	ext2_ioctl,		/* ioctl */
-	generic_file_mmap,	/* mmap */
-#if BITS_PER_LONG == 64	
-	NULL,			/* no special open is needed */
-#else
-	ext2_open_file,
-#endif
-	NULL,			/* flush */
-	ext2_release_file,	/* release */
-	ext2_sync_file,		/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* check_media_change */
-	NULL			/* revalidate */
-};
-
-struct inode_operations ext2_file_inode_operations = {
-	&ext2_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 */
-	ext2_bmap,		/* bmap */
-	ext2_truncate,		/* truncate */
-	ext2_permission,	/* permission */
-	NULL			/* smap */
-};
 
 /*
  * Make sure the offset never goes beyond the 32-bit mark..
@@ -151,164 +106,30 @@
 	}
 }
 
-static ssize_t ext2_file_write (struct file * filp, const char * buf,
-				size_t count, loff_t *ppos)
+static int ext2_writepage (struct file * file, struct page * page)
 {
-	struct inode * inode = filp->f_dentry->d_inode;
-	off_t pos;
-	long block;
-	int offset;
-	int written, c;
-	struct buffer_head * bh, *bufferlist[NBUF];
-	struct super_block * sb;
-	int err;
-	int i,buffercount,write_error;
-
-	/* POSIX: mtime/ctime may not change for 0 count */
-	if (!count)
-		return 0;
-	write_error = buffercount = 0;
-	if (!inode) {
-		printk("ext2_file_write: inode = NULL\n");
-		return -EINVAL;
-	}
-	sb = inode->i_sb;
-	if (sb->s_flags & MS_RDONLY)
-		/*
-		 * This fs has been automatically remounted ro because of errors
-		 */
-		return -ENOSPC;
-
-	if (!S_ISREG(inode->i_mode)) {
-		ext2_warning (sb, "ext2_file_write", "mode = %07o",
-			      inode->i_mode);
-		return -EINVAL;
-	}
-	remove_suid(inode);
-
-	if (filp->f_flags & O_APPEND)
-		pos = inode->i_size;
-	else {
-		pos = *ppos;
-		if (pos != *ppos)
-			return -EINVAL;
-#if BITS_PER_LONG >= 64
-		if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)])
-			return -EINVAL;
-#endif
-	}
+	return block_write_full_page(file, page, ext2_getblk_block);
+}
 
-	/* Check for overflow.. */
-#if BITS_PER_LONG < 64
-	if (pos > (__u32) (pos + count)) {
-		count = ~pos; /* == 0xFFFFFFFF - pos */
-		if (!count)
-			return -EFBIG;
-	}
-#else
-	{
-		off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)];
+static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
+{
+	return block_write_partial_page(file, page, offset, bytes, buf, ext2_getblk_block);
+}
 
-		if (pos + count > max) {
-			count = max - pos;
-			if (!count)
-				return -EFBIG;
-		}
-		if (((pos + count) >> 32) && 
-		    !(sb->u.ext2_sb.s_es->s_feature_ro_compat &
-		      cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) {
-			/* If this is the first large file created, add a flag
-			   to the superblock */
-			sb->u.ext2_sb.s_es->s_feature_ro_compat |=
-				cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
-			mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
-		}
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page);
+	if (retval > 0) {
+		struct inode *inode = file->f_dentry->d_inode;
+		remove_suid(inode);
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+		mark_inode_dirty(inode);
 	}
-#endif
-
-	/*
-	 * If a file has been opened in synchronous mode, we have to ensure
-	 * that meta-data will also be written synchronously.  Thus, we
-	 * set the i_osync field.  This field is tested by the allocation
-	 * routines.
-	 */
-	if (filp->f_flags & O_SYNC)
-		inode->u.ext2_i.i_osync++;
-	block = pos >> EXT2_BLOCK_SIZE_BITS(sb);
-	offset = pos & (sb->s_blocksize - 1);
-	c = sb->s_blocksize - offset;
-	written = 0;
-	do {
-		bh = ext2_getblk (inode, block, 1, &err);
-		if (!bh) {
-			if (!written)
-				written = err;
-			break;
-		}
-		if (c > count)
-			c = count;
-		if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
-			ll_rw_block (READ, 1, &bh);
-			wait_on_buffer (bh);
-			if (!buffer_uptodate(bh)) {
-				brelse (bh);
-				if (!written)
-					written = -EIO;
-				break;
-			}
-		}
-		c -= copy_from_user (bh->b_data + offset, buf, c);
-		if (!c) {
-			brelse(bh);
-			if (!written)
-				written = -EFAULT;
-			break;
-		}
-		update_vm_cache(inode, pos, bh->b_data + offset, c);
-		pos += c;
-		written += c;
-		buf += c;
-		count -= c;
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 0);
-
-		if (filp->f_flags & O_SYNC)
-			bufferlist[buffercount++] = bh;
-		else
-			brelse(bh);
-		if (buffercount == NBUF){
-			ll_rw_block(WRITE, buffercount, bufferlist);
-			for(i=0; i<buffercount; i++){
-				wait_on_buffer(bufferlist[i]);
-				if (!buffer_uptodate(bufferlist[i]))
-					write_error=1;
-				brelse(bufferlist[i]);
-			}
-			buffercount=0;
-		}
-		if(write_error)
-			break;
-		block++;
-		offset = 0;
-		c = sb->s_blocksize;
-	} while (count);
-	if ( buffercount ){
-		ll_rw_block(WRITE, buffercount, bufferlist);
-		for(i=0; i<buffercount; i++){
-			wait_on_buffer(bufferlist[i]);
-			if (!buffer_uptodate(bufferlist[i]))
-				write_error=1;
-			brelse(bufferlist[i]);
-		}
-	}		
-	if (pos > inode->i_size)
-		inode->i_size = pos;
-	if (filp->f_flags & O_SYNC)
-		inode->u.ext2_i.i_osync--;
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	*ppos = pos;
-	mark_inode_dirty(inode);
-	return written;
+	return retval;
 }
 
 /*
@@ -335,3 +156,51 @@
 	return 0;
 }
 #endif
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ext2 filesystem.
+ */
+static struct file_operations ext2_file_operations = {
+	ext2_file_lseek,	/* lseek */
+	generic_file_read,	/* read */
+	ext2_file_write,	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* poll - default */
+	ext2_ioctl,		/* ioctl */
+	generic_file_mmap,	/* mmap */
+#if BITS_PER_LONG == 64	
+	NULL,			/* no special open is needed */
+#else
+	ext2_open_file,
+#endif
+	NULL,			/* flush */
+	ext2_release_file,	/* release */
+	ext2_sync_file,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+struct inode_operations ext2_file_inode_operations = {
+	&ext2_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 */
+	ext2_bmap,		/* bmap */
+	block_read_full_page,	/* readpage */
+	ext2_writepage,		/* writepage */
+	block_flushpage,	/* flushpage */
+	ext2_truncate,		/* truncate */
+	ext2_permission,	/* permission */
+	NULL,			/* smap */
+	NULL,			/* revalidate */
+};
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c
--- v2.3.6/linux/fs/ext2/fsync.c	Wed May 20 13:09:12 1998
+++ linux/fs/ext2/fsync.c	Fri Jun 18 12:58:30 1999
@@ -17,6 +17,9 @@
  *  Removed unnecessary code duplication for little endian machines
  *  and excessive __inline__s. 
  *        Andi Kleen, 1997
+ *
+ * Major simplications and cleanup - we only need to do the metadata, because
+ * we can depend on generic_block_fdatasync() to sync the data blocks.
  */
 
 #include <asm/uaccess.h>
@@ -32,221 +35,84 @@
 #include <linux/locks.h>
 
 
-#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
-#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
+#define blocksize	(EXT2_BLOCK_SIZE(inode->i_sb))
+#define addr_per_block	(EXT2_ADDR_PER_BLOCK(inode->i_sb))
 
-static int sync_block (struct inode * inode, u32 * block, int wait)
+static int sync_indirect(struct inode * inode, u32 * block, int wait)
 {
 	struct buffer_head * bh;
 	
 	if (!*block)
 		return 0;
-	bh = get_hash_table (inode->i_dev, *block, blocksize);
+	bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize);
 	if (!bh)
 		return 0;
 	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-		brelse (bh);
+		brelse(bh);
 		return -1;
 	}
 	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
-		brelse (bh);
+		brelse(bh);
 		return 0;
 	}
-	ll_rw_block (WRITE, 1, &bh);
+	ll_rw_block(WRITE, 1, &bh);
 	bh->b_count--;
 	return 0;
 }
 
-#ifndef __LITTLE_ENDIAN
-static int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
-{
-	struct buffer_head * bh;
-	
-	if (!le32_to_cpu(*block))
-		return 0;
-	bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
-	if (!bh)
-		return 0;
-	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-		brelse (bh);
-		return -1;
-	}
-	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
-		brelse (bh);
-		return 0;
-	}
-	ll_rw_block (WRITE, 1, &bh);
-	bh->b_count--;
-	return 0;
-}
-#else
-#define sync_block_swab32 sync_block
-#endif
-
-
-static int sync_iblock (struct inode * inode, u32 * iblock, 
+static int sync_iblock(struct inode * inode, u32 * iblock, 
 			struct buffer_head ** bh, int wait) 
 {
 	int rc, tmp;
 	
 	*bh = NULL;
-	tmp = *iblock;
-	if (!tmp)
-		return 0;
-	rc = sync_block (inode, iblock, wait);
-	if (rc)
-		return rc;
-	*bh = bread (inode->i_dev, tmp, blocksize);
-	if (!*bh)
-		return -1;
-	return 0;
-}
-
-#ifndef __LITTLE_ENDIAN
-static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, 
-			       struct buffer_head ** bh, int wait) 
-{
-	int rc, tmp;
-	
-	*bh = NULL;
 	tmp = le32_to_cpu(*iblock);
 	if (!tmp)
 		return 0;
-	rc = sync_block_swab32 (inode, iblock, wait);
+	rc = sync_indirect(inode, iblock, wait);
 	if (rc)
 		return rc;
-	*bh = bread (inode->i_dev, tmp, blocksize);
+	*bh = bread(inode->i_dev, tmp, blocksize);
 	if (!*bh)
 		return -1;
 	return 0;
 }
-#else
-#define sync_iblock_swab32 sync_iblock
-#endif
 
-static int sync_direct (struct inode * inode, int wait)
-{
-	int i;
-	int rc, err = 0;
-
-	for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
-		rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait);
-		if (rc)
-			err = rc;
-	}
-	return err;
-}
-
-static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
-{
-	int i;
-	struct buffer_head * ind_bh;
-	int rc, err = 0;
-
-	rc = sync_iblock (inode, iblock, &ind_bh, wait);
-	if (rc || !ind_bh)
-		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_block_swab32 (inode, 
-					((u32 *) ind_bh->b_data) + i,
-					wait);
-		if (rc)
-			err = rc;
-	}
-	brelse (ind_bh);
-	return err;
-}
-
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
-{
-	int i;
-	struct buffer_head * ind_bh;
-	int rc, err = 0;
-
-	rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
-	if (rc || !ind_bh)
-		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_block_swab32 (inode, 
-					((u32 *) ind_bh->b_data) + i,
-					wait);
-		if (rc)
-			err = rc;
-	}
-	brelse (ind_bh);
-	return err;
-}
-#else
-#define sync_indirect_swab32 sync_indirect
-#endif
-
-static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
-{
-	int i;
-	struct buffer_head * dind_bh;
-	int rc, err = 0;
-
-	rc = sync_iblock (inode, diblock, &dind_bh, wait);
-	if (rc || !dind_bh)
-		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_indirect_swab32 (inode,
-					   ((u32 *) dind_bh->b_data) + i,
-					   wait);
-		if (rc)
-			err = rc;
-	}
-	brelse (dind_bh);
-	return err;
-}
-
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
+static int sync_dindirect(struct inode * inode, u32 * diblock, int wait)
 {
 	int i;
 	struct buffer_head * dind_bh;
 	int rc, err = 0;
 
-	rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
+	rc = sync_iblock(inode, diblock, &dind_bh, wait);
 	if (rc || !dind_bh)
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_indirect_swab32 (inode,
-					   ((u32 *) dind_bh->b_data) + i,
-					   wait);
+		rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait);
 		if (rc)
 			err = rc;
 	}
-	brelse (dind_bh);
+	brelse(dind_bh);
 	return err;
 }
-#else
-#define sync_dindirect_swab32 sync_dindirect
-#endif
 
-static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
+static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait)
 {
 	int i;
 	struct buffer_head * tind_bh;
 	int rc, err = 0;
 
-	rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+	rc = sync_iblock(inode, tiblock, &tind_bh, wait);
 	if (rc || !tind_bh)
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_dindirect_swab32 (inode,
-					    ((u32 *) tind_bh->b_data) + i,
-					    wait);
+		rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait);
 		if (rc)
 			err = rc;
 	}
-	brelse (tind_bh);
+	brelse(tind_bh);
 	return err;
 }
 
@@ -266,18 +132,19 @@
 		 */
 		goto skip;
 
+	err = generic_buffer_fdatasync(inode, 0, ~0UL);
+
 	for (wait=0; wait<=1; wait++)
 	{
-		err |= sync_direct (inode, wait);
-		err |= sync_indirect (inode,
-				      inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
+		err |= sync_indirect(inode,
+				     inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
+				     wait);
+		err |= sync_dindirect(inode,
+				      inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
+				      wait);
+		err |= sync_tindirect(inode, 
+				      inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
 				      wait);
-		err |= sync_dindirect (inode,
-				       inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
-				       wait);
-		err |= sync_tindirect (inode, 
-				       inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
-				       wait);
 	}
 skip:
 	err |= ext2_sync_inode (inode);
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.3.6/linux/fs/ext2/inode.c	Tue May 11 23:01:41 1999
+++ linux/fs/ext2/inode.c	Sun Jun 20 15:58:20 1999
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/mm.h>
+#include <linux/smp_lock.h>
 
 static int ext2_update_inode(struct inode * inode, int do_sync);
 
@@ -59,7 +60,7 @@
 	ext2_free_inode (inode);
 }
 
-#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
+#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)]))
 
 static inline int block_bmap (struct buffer_head * bh, int nr)
 {
@@ -92,13 +93,12 @@
 #endif
 }
 
-static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
 {
 #ifdef EXT2FS_DEBUG
 	static unsigned long alloc_hits = 0, alloc_attempts = 0;
 #endif
 	unsigned long result;
-	struct buffer_head * bh;
 
 	wait_on_super (inode->i_sb);
 
@@ -112,19 +112,6 @@
 		ext2_debug ("preallocation hit (%lu/%lu).\n",
 			    ++alloc_hits, ++alloc_attempts);
 
-		/* It doesn't matter if we block in getblk() since
-		   we have already atomically allocated the block, and
-		   are only clearing it now. */
-		if (!(bh = getblk (inode->i_sb->s_dev, result,
-				   inode->i_sb->s_blocksize))) {
-			ext2_error (inode->i_sb, "ext2_alloc_block",
-				    "cannot get block %lu", result);
-			return 0;
-		}
-		memset(bh->b_data, 0, inode->i_sb->s_blocksize);
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 1);
-		brelse (bh);
 	} else {
 		ext2_discard_prealloc (inode);
 		ext2_debug ("preallocation miss (%lu/%lu).\n",
@@ -139,13 +126,76 @@
 #else
 	result = ext2_new_block (inode, goal, 0, 0, err);
 #endif
-
 	return result;
 }
 
 
 int ext2_bmap (struct inode * inode, int block)
 {
+	int i, ret;
+	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+
+	ret = 0;
+	lock_kernel();
+	if (block < 0) {
+		ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
+		goto out;
+	}
+	if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
+		(1 << (addr_per_block_bits * 2)) +
+		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
+		ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
+		goto out;
+	}
+	if (block < EXT2_NDIR_BLOCKS) {
+		ret = inode_bmap (inode, block);
+		goto out;
+	}
+	block -= EXT2_NDIR_BLOCKS;
+	if (block < addr_per_block) {
+		i = inode_bmap (inode, EXT2_IND_BLOCK);
+		if (!i)
+			goto out;
+		ret = block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize), block);
+		goto out;
+	}
+	block -= addr_per_block;
+	if (block < (1 << (addr_per_block_bits * 2))) {
+		i = inode_bmap (inode, EXT2_DIND_BLOCK);
+		if (!i)
+			goto out;
+		i = block_bmap (bread (inode->i_dev, i,
+				       inode->i_sb->s_blocksize),
+				block >> addr_per_block_bits);
+		if (!i)
+			goto out;
+		ret = block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize),
+				block & (addr_per_block - 1));
+	}
+	block -= (1 << (addr_per_block_bits * 2));
+	i = inode_bmap (inode, EXT2_TIND_BLOCK);
+	if (!i)
+		goto out;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			block >> (addr_per_block_bits * 2));
+	if (!i)
+		goto out;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			(block >> addr_per_block_bits) & (addr_per_block - 1));
+	if (!i)
+		goto out;
+	ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			   block & (addr_per_block - 1));
+out:
+	unlock_kernel();
+	return ret;
+}
+
+int ext2_bmap_create (struct inode * inode, int block)
+{
 	int i;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
@@ -201,7 +251,8 @@
 }
 
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
-					  int create, int new_block, int * err)
+	int create, int new_block, int * err, int metadata,
+	int *phys_block, int *created)
 {
 	u32 * p;
 	int tmp, goal = 0;
@@ -210,13 +261,18 @@
 
 	p = inode->u.ext2_i.i_data + nr;
 repeat:
-	tmp = *p;
+	tmp = le32_to_cpu(*p);
 	if (tmp) {
-		struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-		if (tmp == *p)
-			return result;
-		brelse (result);
-		goto repeat;
+		if (metadata) {
+			struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+			if (tmp == le32_to_cpu(*p))
+				return result;
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
+		}
 	}
 	*err = -EFBIG;
 	if (!create)
@@ -244,7 +300,7 @@
 	if (!goal) {
 		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (inode->u.ext2_i.i_data[tmp]) {
-				goal = inode->u.ext2_i.i_data[tmp];
+				goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]);
 				break;
 			}
 		}
@@ -259,13 +315,28 @@
 	tmp = ext2_alloc_block (inode, goal, err);
 	if (!tmp)
 		return NULL;
-	result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (*p) {
-		ext2_free_blocks (inode, tmp, 1);
-		brelse (result);
-		goto repeat;
+	if (metadata) {
+		result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			brelse (result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, inode->i_sb->s_blocksize);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
 	}
-	*p = tmp;
+	*p = cpu_to_le32(tmp);
+
 	inode->u.ext2_i.i_next_alloc_block = new_block;
 	inode->u.ext2_i.i_next_alloc_goal = tmp;
 	inode->i_ctime = CURRENT_TIME;
@@ -277,10 +348,17 @@
 	return result;
 }
 
+/*
+ *   metadata / data
+ *   possibly create / access
+ *   can fail due to: - not present
+ *                    - out of space
+ *
+ *   NULL return in the data case is mandatory.
+ */
 static struct buffer_head * block_getblk (struct inode * inode,
-					  struct buffer_head * bh, int nr,
-					  int create, int blocksize, 
-					  int new_block, int * err)
+	  struct buffer_head * bh, int nr, int create, int blocksize, 
+	  int new_block, int * err, int metadata, int *phys_block, int *created)
 {
 	int tmp, goal = 0;
 	u32 * p;
@@ -302,13 +380,19 @@
 repeat:
 	tmp = le32_to_cpu(*p);
 	if (tmp) {
-		result = getblk (bh->b_dev, tmp, blocksize);
-		if (tmp == le32_to_cpu(*p)) {
+		if (metadata) {
+			result = getblk (bh->b_dev, tmp, blocksize);
+			if (tmp == le32_to_cpu(*p)) {
+				brelse (bh);
+				return result;
+			}
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse (bh);
-			return result;
+			return NULL;
 		}
-		brelse (result);
-		goto repeat;
 	}
 	*err = -EFBIG;
 	if (!create) {
@@ -343,7 +427,22 @@
 		brelse (bh);
 		return NULL;
 	}
-	result = getblk (bh->b_dev, tmp, blocksize);
+	if (metadata) {
+		result = getblk (bh->b_dev, tmp, blocksize);
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			brelse (result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, inode->i_sb->s_blocksize);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
+	}
 	if (le32_to_cpu(*p)) {
 		ext2_free_blocks (inode, tmp, 1);
 		brelse (result);
@@ -364,24 +463,27 @@
 	return result;
 }
 
-struct buffer_head * ext2_getblk (struct inode * inode, long block,
-				  int create, int * err)
+int ext2_getblk_block (struct inode * inode, long block,
+				  int create, int * err, int * created)
 {
-	struct buffer_head * bh;
+	struct buffer_head * bh, *tmp;
 	unsigned long b;
 	unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+	int phys_block, ret;
 
+	lock_kernel();
+	ret = 0;
 	*err = -EIO;
 	if (block < 0) {
 		ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
-		return NULL;
+		goto abort;
 	}
 	if (block > EXT2_NDIR_BLOCKS + addr_per_block +
 		(1 << (addr_per_block_bits * 2)) +
 		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
 		ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
-		return NULL;
+		goto abort;
 	}
 	/*
 	 * If this is a sequential block allocation, set the next_alloc_block
@@ -398,32 +500,72 @@
 		inode->u.ext2_i.i_next_alloc_goal++;
 	}
 
-	*err = -ENOSPC;
+	*err = 0; // -ENOSPC;
 	b = block;
-	if (block < EXT2_NDIR_BLOCKS)
-		return inode_getblk (inode, block, create, b, err);
+	*created = 0;
+	if (block < EXT2_NDIR_BLOCKS) {
+		/*
+		 * data page.
+		 */
+		tmp = inode_getblk (inode, block, create, b,
+					err, 0, &phys_block, created);
+		goto out;
+	}
 	block -= EXT2_NDIR_BLOCKS;
 	if (block < addr_per_block) {
-		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err);
-		return block_getblk (inode, bh, block, create,
-				     inode->i_sb->s_blocksize, b, err);
+		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL);
+		tmp = block_getblk (inode, bh, block, create,
+		     inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+		goto out;
 	}
 	block -= addr_per_block;
 	if (block < (1 << (addr_per_block_bits * 2))) {
-		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
+		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL);
 		bh = block_getblk (inode, bh, block >> addr_per_block_bits,
-				   create, inode->i_sb->s_blocksize, b, err);
-		return block_getblk (inode, bh, block & (addr_per_block - 1),
-				     create, inode->i_sb->s_blocksize, b, err);
+				   create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL);
+		tmp = block_getblk (inode, bh, block & (addr_per_block - 1),
+		     create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+		goto out;
 	}
 	block -= (1 << (addr_per_block_bits * 2));
-	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
+	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL);
 	bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
-			   create, inode->i_sb->s_blocksize, b, err);
-	bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
-			   create, inode->i_sb->s_blocksize, b, err);
-	return block_getblk (inode, bh, block & (addr_per_block - 1), create,
-			     inode->i_sb->s_blocksize, b, err);
+			   create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL);
+	bh = block_getblk (inode, bh, (block >> addr_per_block_bits) &
+		(addr_per_block - 1), create, inode->i_sb->s_blocksize,
+		b, err, 1, NULL,NULL);
+	tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create,
+		inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+
+out:
+	if (!phys_block)
+		goto abort;
+	if (*err)
+		goto abort;
+	ret = phys_block;
+abort:
+	unlock_kernel();
+	return ret;
+}
+
+struct buffer_head * ext2_getblk (struct inode * inode, long block,
+				  int create, int * err)
+{
+	struct buffer_head *tmp = NULL;
+	int phys_block;
+	int created;
+
+	phys_block = ext2_getblk_block (inode, block, create, err, &created);
+
+	if (phys_block) {
+		tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize);
+		if (created) {
+			memset(tmp->b_data, 0, inode->i_sb->s_blocksize);
+			mark_buffer_uptodate(tmp, 1);
+			mark_buffer_dirty(tmp, 1);
+		}
+	}
+	return tmp;
 }
 
 struct buffer_head * ext2_bread (struct inode * inode, int block, 
@@ -569,11 +711,14 @@
 	if (inode->u.ext2_i.i_prealloc_count)
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "New inode has non-zero prealloc count!");
-	if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
-	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]);
+
+	/*
+	 * NOTE! The in-memory inode i_blocks array is in little-endian order
+	 * even on big-endian machines: we do NOT byteswap the block numbers!
+	 */
+	for (block = 0; block < EXT2_N_BLOCKS; block++)
+		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+
 	if (inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		/* Nothing to do */ ;
@@ -689,11 +834,8 @@
 	raw_inode->i_generation = cpu_to_le32(inode->i_generation);
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
-	else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]);
+		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	mark_buffer_dirty(bh, 1);
 	if (do_sync) {
 		ll_rw_block (WRITE, 1, &bh);
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.3.6/linux/fs/ext2/symlink.c	Wed Jun  9 20:46:53 1999
+++ linux/fs/ext2/symlink.c	Sat Jun 19 11:45:29 1999
@@ -43,12 +43,14 @@
 	NULL,			/* rename */
 	ext2_readlink,		/* readlink */
 	ext2_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
 	NULL,			/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static struct dentry * ext2_follow_link(struct dentry * dentry,
diff -u --recursive --new-file v2.3.6/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v2.3.6/linux/fs/ext2/truncate.c	Sun May 16 13:55:46 1999
+++ linux/fs/ext2/truncate.c	Fri Jun 18 10:39:56 1999
@@ -131,10 +131,7 @@
 
 	if (bh->b_count == 1) {
 		int tmp;
-		if (ind_bh)
-			tmp = le32_to_cpu(*p);
-		else
-			tmp = *p;
+		tmp = le32_to_cpu(*p);
 		*p = 0;
 		inode->i_blocks -= (inode->i_sb->s_blocksize / 512);
 		mark_inode_dirty(inode);
@@ -160,6 +157,9 @@
 	return retry;
 }
 
+#define DATA_BUFFER_USED(bh) \
+	((bh->b_count > 1) || buffer_locked(bh))
+
 static int trunc_direct (struct inode * inode)
 {
 	struct buffer_head * bh;
@@ -170,7 +170,7 @@
 
 	for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
 		u32 * p = inode->u.ext2_i.i_data + i;
-		int tmp = *p;
+		int tmp = le32_to_cpu(*p);
 
 		if (!tmp)
 			continue;
@@ -178,7 +178,7 @@
 		bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize);
 		if (bh) {
 			bh->b_count++;
-			if(bh->b_count != 1 || buffer_locked(bh)) {
+			if (DATA_BUFFER_USED(bh)) {
 				brelse(bh);
 				retry = 1;
 				continue;
@@ -215,11 +215,11 @@
 	unsigned long block_to_free = 0, free_count = 0;
 	int indirect_block, addr_per_block, blocks;
 
-	tmp = dind_bh ? le32_to_cpu(*p) : *p;
+	tmp = le32_to_cpu(*p);
 	if (!tmp)
 		return 0;
 	ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) {
+	if (tmp != le32_to_cpu(*p)) {
 		brelse (ind_bh);
 		return 1;
 	}
@@ -255,8 +255,8 @@
 		bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize);
 		if (bh) {
 			bh->b_count++;
-			if (bh->b_count != 1 || buffer_locked(bh)) {
-				brelse (bh);
+			if (DATA_BUFFER_USED(bh)) {
+				brelse(bh);
 				retry = 1;
 				continue;
 			}
@@ -297,11 +297,11 @@
 	int i, tmp, retry = 0;
 	int dindirect_block, addr_per_block;
 
-	tmp = tind_bh ? le32_to_cpu(*p) : *p;
+	tmp = le32_to_cpu(*p);
 	if (!tmp)
 		return 0;
 	dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) {
+	if (tmp != le32_to_cpu(*p)) {
 		brelse (dind_bh);
 		return 1;
 	}
@@ -344,10 +344,11 @@
 	int i, tmp, retry = 0;
 	int tindirect_block, addr_per_block, offset;
 
-	if (!(tmp = *p))
+	tmp = le32_to_cpu(*p);
+	if (!tmp)
 		return 0;
 	tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (tmp != *p) {
+	if (tmp != le32_to_cpu(*p)) {
 		brelse (tind_bh);
 		return 1;
 	}
@@ -384,8 +385,6 @@
 		
 void ext2_truncate (struct inode * inode)
 {
-	int err, offset;
-
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 	    S_ISLNK(inode->i_mode)))
 		return;
@@ -410,25 +409,6 @@
 		run_task_queue(&tq_disk);
 		current->policy |= SCHED_YIELD;
 		schedule();
-	}
-	/*
-	 * If the file is not being truncated to a block boundary, the
-	 * contents of the partial block following the end of the file
-	 * must be zeroed in case it ever becomes accessible again due
-	 * to subsequent file growth.
-	 */
-	offset = inode->i_size & (inode->i_sb->s_blocksize - 1);
-	if (offset) {
-		struct buffer_head * bh;
-		bh = ext2_bread (inode,
-				 inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb),
-				 0, &err);
-		if (bh) {
-			memset (bh->b_data + offset, 0,
-				inode->i_sb->s_blocksize - offset);
-			mark_buffer_dirty (bh, 0);
-			brelse (bh);
-		}
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
diff -u --recursive --new-file v2.3.6/linux/fs/fifo.c linux/fs/fifo.c
--- v2.3.6/linux/fs/fifo.c	Tue May 11 14:37:40 1999
+++ linux/fs/fifo.c	Sat Jun 19 11:45:28 1999
@@ -2,14 +2,45 @@
  *  linux/fs/fifo.c
  *
  *  written by Paul H. Hargrove
+ *
+ *  Fixes:
+ *	10-06-1999, AV: fixed OOM handling in fifo_open(), moved
+ *			initialization there, switched to external
+ *			allocation of pipe_inode_info.
  */
 
 #include <linux/mm.h>
+#include <linux/malloc.h>
 
 static int fifo_open(struct inode * inode,struct file * filp)
 {
 	int retval = 0;
-	unsigned long page;
+	unsigned long page = 0;
+	struct pipe_inode_info *info, *tmp = NULL;
+
+	if (inode->i_pipe)
+		goto got_it;
+	tmp = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL);
+	if (inode->i_pipe)
+		goto got_it;
+	if (!tmp)
+		goto oom;
+	page = __get_free_page(GFP_KERNEL);
+	if (inode->i_pipe)
+		goto got_it;
+	if (!page)
+		goto oom;
+	inode->i_pipe = tmp;
+	PIPE_LOCK(*inode) = 0;
+	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
+	PIPE_BASE(*inode) = (char *) page;
+	PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
+	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+	init_waitqueue_head(&PIPE_WAIT(*inode));
+	tmp = NULL;	/* no need to free it */
+	page = 0;
+
+got_it:
 
 	switch( filp->f_mode ) {
 
@@ -94,19 +125,26 @@
 	default:
 		retval = -EINVAL;
 	}
-	if (retval || PIPE_BASE(*inode))
-		return retval;
-	page = __get_free_page(GFP_KERNEL);
-	if (PIPE_BASE(*inode)) {
+	if (retval) 
+		goto cleanup;
+out:
+	if (tmp)
+		kfree(tmp);
+	if (page)
 		free_page(page);
-		return 0;
+	return retval;
+
+cleanup:
+	if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
+		info = inode->i_pipe;
+		inode->i_pipe = NULL;
+		free_page((unsigned long)info->base);
+		kfree(info);
 	}
-	if (!page)
-		return -ENOMEM;
-	PIPE_LOCK(*inode) = 0;
-	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
-	PIPE_BASE(*inode) = (char *) page;
-	return 0;
+	goto out;
+oom:
+	retval = -ENOMEM;
+	goto out;
 }
 
 /*
@@ -141,20 +179,20 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
+
+/* Goner. Filesystems do not use it anymore. */
+
 void init_fifo(struct inode * inode)
 {
 	inode->i_op = &fifo_inode_operations;
-	PIPE_LOCK(*inode) = 0;
-	PIPE_BASE(*inode) = NULL;
-	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
-	PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
-	init_waitqueue_head(&PIPE_WAIT(*inode));
-	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
 }
diff -u --recursive --new-file v2.3.6/linux/fs/hfs/dir_nat.c linux/fs/hfs/dir_nat.c
--- v2.3.6/linux/fs/hfs/dir_nat.c	Thu May 13 10:53:59 1999
+++ linux/fs/hfs/dir_nat.c	Sat Jun 19 11:45:28 1999
@@ -99,7 +99,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,                   /* updatepage */
 	NULL                    /* revalidate */
 };
 
@@ -122,7 +121,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,                   /* updatepage */
 	NULL                    /* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file.c linux/fs/hfs/file.c
--- v2.3.6/linux/fs/hfs/file.c	Mon Nov  2 09:35:16 1998
+++ linux/fs/hfs/file.c	Sat Jun 19 11:45:28 1999
@@ -69,7 +69,6 @@
 	hfs_file_truncate,	/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c
--- v2.3.6/linux/fs/hfs/file_cap.c	Mon Nov  2 09:35:16 1998
+++ linux/fs/hfs/file_cap.c	Sat Jun 19 11:45:28 1999
@@ -83,7 +83,6 @@
 	cap_info_truncate,		/* truncate */
 	NULL,				/* permission */
 	NULL,				/* smap */
-	NULL,				/* updatepage */
 	NULL				/* revalidata */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
--- v2.3.6/linux/fs/hfs/file_hdr.c	Wed May 12 13:26:20 1999
+++ linux/fs/hfs/file_hdr.c	Sat Jun 19 11:45:28 1999
@@ -85,7 +85,6 @@
 	hdr_truncate,		/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/hpfs/inode.c linux/fs/hpfs/inode.c
--- v2.3.6/linux/fs/hpfs/inode.c	Sun May 16 10:27:40 1999
+++ linux/fs/hpfs/inode.c	Sat Jun 19 11:45:28 1999
@@ -48,7 +48,6 @@
 	&hpfs_truncate,			/* truncate */
 	NULL,				/* permission */
 	NULL,				/* smap */
-	NULL,				/* updatepage */
 	NULL,				/* revalidate */
 };
 
@@ -91,7 +90,6 @@
 	NULL,				/* truncate */
 	NULL,				/* permission */
 	NULL,				/* smap */
-	NULL,				/* updatepage */
 	NULL,				/* revalidate */
 };
 
@@ -115,7 +113,6 @@
 	NULL,				/* truncate */
 	NULL,				/* permission */
 	NULL,				/* smap */
-	NULL,				/* updatepage */
 	NULL,				/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/inode.c linux/fs/inode.c
--- v2.3.6/linux/fs/inode.c	Thu May 13 23:18:20 1999
+++ linux/fs/inode.c	Thu Jun 17 23:11:01 1999
@@ -130,7 +130,6 @@
 	INIT_LIST_HEAD(&inode->i_hash);
 	INIT_LIST_HEAD(&inode->i_dentry);
 	sema_init(&inode->i_sem, 1);
-	sema_init(&inode->i_atomic_write, 1);
 }
 
 static inline void write_inode(struct inode *inode)
@@ -337,7 +336,7 @@
  *      dispose_list.
  */
 #define CAN_UNUSE(inode) \
-	(((inode)->i_count | (inode)->i_state) == 0)
+	(((inode)->i_count | (inode)->i_state | (inode)->i_nrpages) == 0)
 #define INODE(entry)	(list_entry(entry, struct inode, i_list))
 
 static int free_inodes(void)
@@ -527,6 +526,7 @@
 	inode->i_generation = 0;
 	memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
 	sema_init(&inode->i_sem, 1);
+	inode->i_pipe = NULL;
 }
 
 /*
@@ -765,9 +765,6 @@
 kdevname(inode->i_dev), inode->i_ino, inode->i_count);
 if (atomic_read(&inode->i_sem.count) != 1)
 printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n",
-kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
-if (atomic_read(&inode->i_atomic_write.count) != 1)
-printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n",
 kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
 #endif
 		}
diff -u --recursive --new-file v2.3.6/linux/fs/isofs/file.c linux/fs/isofs/file.c
--- v2.3.6/linux/fs/isofs/file.c	Sun Mar  7 15:25:23 1999
+++ linux/fs/isofs/file.c	Sat Jun 19 18:20:13 1999
@@ -48,9 +48,10 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	generic_readpage,	/* readpage */
-	NULL,			/* writepage */
 	isofs_bmap,		/* bmap */
+	block_read_full_page,	/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* flushpage */
 	NULL,	       		/* truncate */
 	NULL			/* permission */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.3.6/linux/fs/isofs/inode.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/isofs/inode.c	Sat Jun 19 18:20:13 1999
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/nls.h>
 #include <linux/ctype.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -909,7 +910,7 @@
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
-int isofs_bmap(struct inode * inode,int block)
+static int do_isofs_bmap(struct inode * inode,int block)
 {
 	off_t b_off, offset, size;
 	struct inode *ino;
@@ -991,6 +992,15 @@
 	return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode);
 }
 
+int isofs_bmap(struct inode * inode,int block)
+{
+	int retval;
+
+	lock_kernel();
+	retval = do_isofs_bmap(inode, block);
+	unlock_kernel();
+	return retval;
+}
 
 static void test_and_set_uid(uid_t *p, uid_t value)
 {
diff -u --recursive --new-file v2.3.6/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- v2.3.6/linux/fs/minix/bitmap.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/minix/bitmap.c	Wed Jun 16 19:26:27 1999
@@ -112,14 +112,6 @@
 	if (j < sb->u.minix_sb.s_firstdatazone ||
 	    j >= sb->u.minix_sb.s_nzones)
 		return 0;
-	if (!(bh = getblk(sb->s_dev,j,BLOCK_SIZE))) {
-		printk("new_block: cannot get block");
-		return 0;
-	}
-	memset(bh->b_data, 0, BLOCK_SIZE);
-	mark_buffer_uptodate(bh, 1);
-	mark_buffer_dirty(bh, 1);
-	brelse(bh);
 	return j;
 }
 
diff -u --recursive --new-file v2.3.6/linux/fs/minix/file.c linux/fs/minix/file.c
--- v2.3.6/linux/fs/minix/file.c	Mon Aug 24 13:02:44 1998
+++ linux/fs/minix/file.c	Sat Jun 19 12:15:14 1999
@@ -27,7 +27,51 @@
 #include <linux/fs.h>
 #include <linux/minix_fs.h>
 
-static ssize_t minix_file_write(struct file *, const char *, size_t, loff_t *);
+static int minix_writepage(struct file *file, struct page *page)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	unsigned long block;
+	int *p, nr[PAGE_SIZE/BLOCK_SIZE];
+	int i, err, created;
+	struct buffer_head *bh;
+
+	i = PAGE_SIZE / BLOCK_SIZE;
+	block = page->offset / BLOCK_SIZE;
+	p = nr;
+	bh = page->buffers;
+	do {
+		if (bh && bh->b_blocknr)
+			*p = bh->b_blocknr;
+		else
+			*p = minix_getblk_block(inode, block, 1, &err, &created);
+		if (!*p)
+			return -EIO;
+		i--;
+		block++;
+		p++;
+		if (bh)
+			bh = bh->b_this_page;
+	} while(i > 0);
+
+	/* IO start */
+	brw_page(WRITE, page, inode->i_dev, nr, BLOCK_SIZE, 1);
+	return 0;
+}
+
+static long minix_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf)
+{
+	return block_write_one_page(file, page, offset, bytes, buf, minix_getblk_block);
+}
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+minix_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	return generic_file_write(file, buf, count, ppos, minix_write_one_page);
+}
 
 /*
  * We have mostly NULLs here: the current defaults are OK for
@@ -61,74 +105,11 @@
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	generic_readpage,	/* readpage */
-	NULL,			/* writepage */
+	minix_writepage,	/* writepage */
 	minix_bmap,		/* bmap */
 	minix_truncate,		/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* revalidate */
+	block_flushpage,	/* flushpage */
 };
-
-static ssize_t minix_file_write(struct file * filp, const char * buf,
-				size_t count, loff_t *ppos)
-{
-	struct inode * inode = filp->f_dentry->d_inode;
-	off_t pos;
-	ssize_t written, c;
-	struct buffer_head * bh;
-	char * p;
-
-	if (!inode) {
-		printk("minix_file_write: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!S_ISREG(inode->i_mode)) {
-		printk("minix_file_write: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	if (filp->f_flags & O_APPEND)
-		pos = inode->i_size;
-	else
-		pos = *ppos;
-	written = 0;
-	while (written < count) {
-		bh = minix_getblk(inode,pos/BLOCK_SIZE,1);
-		if (!bh) {
-			if (!written)
-				written = -ENOSPC;
-			break;
-		}
-		c = BLOCK_SIZE - (pos % BLOCK_SIZE);
-		if (c > count-written)
-			c = count-written;
-		if (c != BLOCK_SIZE && !buffer_uptodate(bh)) {
-			ll_rw_block(READ, 1, &bh);
-			wait_on_buffer(bh);
-			if (!buffer_uptodate(bh)) {
-				brelse(bh);
-				if (!written)
-					written = -EIO;
-				break;
-			}
-		}
-		p = (pos % BLOCK_SIZE) + bh->b_data;
-		c -= copy_from_user(p,buf,c);
-		if (!c) {
-			brelse(bh);
-			if (!written)
-				written = -EFAULT;
-			break;
-		}
-		update_vm_cache(inode, pos, p, c);
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 0);
-		brelse(bh);
-		pos += c;
-		written += c;
-		buf += c;
-	}
-	if (pos > inode->i_size)
-		inode->i_size = pos;
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	*ppos = pos;
-	mark_inode_dirty(inode);
-	return written;
-}
diff -u --recursive --new-file v2.3.6/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v2.3.6/linux/fs/minix/inode.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/minix/inode.c	Wed Jun 16 19:26:27 1999
@@ -407,7 +407,7 @@
 	return tmp;
 }
 
-static int V2_minix_bmap(struct inode * inode,int block)
+static int V2_minix_bmap(struct inode * inode, int block)
 {
 	int i;
 
@@ -454,7 +454,7 @@
 /*
  * The global minix fs bmap function.
  */
-int minix_bmap(struct inode * inode,int block)
+int minix_bmap(struct inode * inode, int block)
 {
 	if (INODE_VERSION(inode) == MINIX_V1)
 		return V1_minix_bmap(inode, block);
@@ -465,8 +465,8 @@
 /*
  * The minix V1 fs getblk functions.
  */
-static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr,
-					    int create)
+static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, int create,
+					    int metadata, int *phys_block, int *created)
 {
 	int tmp;
 	unsigned short *p;
@@ -476,31 +476,51 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
-		if (tmp == *p)
-			return result;
-		brelse(result);
-		goto repeat;
+		if (metadata) {
+			result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+			if (tmp == *p)
+				return result;
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
+		}
 	}
 	if (!create)
 		return NULL;
 	tmp = minix_new_block(inode->i_sb);
 	if (!tmp)
 		return NULL;
-	result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
-	if (*p) {
-		minix_free_block(inode->i_sb,tmp);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			brelse(result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, BLOCK_SIZE);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
 	*p = tmp;
+
 	inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
 	return result;
 }
 
 static struct buffer_head * V1_block_getblk(struct inode * inode,
-	struct buffer_head * bh, int nr, int create)
+	struct buffer_head * bh, int nr, int create,
+	int metadata, int *phys_block, int *created)
 {
 	int tmp;
 	unsigned short *p;
@@ -520,13 +540,19 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
-		if (tmp == *p) {
+		if (metadata) {
+			result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+			if (tmp == *p) {
+				brelse(bh);
+				return result;
+			}
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse(bh);
-			return result;
+			return NULL;
 		}
-		brelse(result);
-		goto repeat;
 	}
 	if (!create) {
 		brelse(bh);
@@ -537,49 +563,74 @@
 		brelse(bh);
 		return NULL;
 	}
-	result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
-	if (*p) {
-		minix_free_block(inode->i_sb,tmp);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			brelse(result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, BLOCK_SIZE);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
+
 	*p = tmp;
 	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	return result;
 }
 
-static struct buffer_head * V1_minix_getblk(struct inode * inode, int block,
-					    int create)
+int V1_getblk_block(struct inode * inode, long block, int create, int *err, int *created)
 {
-	struct buffer_head * bh;
+	struct buffer_head *bh, *tmp;
+	int phys_block;
 
-	if (block<0) {
+	*err = -EIO;
+	if (block < 0) {
 		printk("minix_getblk: block<0");
-		return NULL;
+		return 0;
 	}
 	if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
 		printk("minix_getblk: block>big");
-		return NULL;
+		return 0;
+	}
+	*created = 0;
+	if (block < 7) {
+		tmp = V1_inode_getblk(inode, block, create,
+				      0, &phys_block, created);
+		goto out;
 	}
-	if (block < 7)
-		return V1_inode_getblk(inode,block,create);
 	block -= 7;
 	if (block < 512) {
-		bh = V1_inode_getblk(inode,7,create);
-		return V1_block_getblk(inode, bh, block, create);
+		bh = V1_inode_getblk(inode, 7, create, 1, NULL, NULL);
+		tmp = V1_block_getblk(inode, bh, block, create,
+				      0, &phys_block, created);
+		goto out;
 	}
 	block -= 512;
-	bh = V1_inode_getblk(inode,8,create);
-	bh = V1_block_getblk(inode, bh, (block>>9) & 511, create);
-	return V1_block_getblk(inode, bh, block & 511, create);
+	bh = V1_inode_getblk(inode, 8, create, 1, NULL, NULL);
+	bh = V1_block_getblk(inode, bh, (block>>9) & 511, create, 1, NULL, NULL);
+	tmp = V1_block_getblk(inode, bh, block & 511, create, 0, &phys_block, created);
+
+out:
+	*err = 0;
+	return phys_block;
 }
 
 /*
  * The minix V2 fs getblk functions.
  */
-static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr,
-					    int create)
+static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, int create,
+					    int metadata, int *phys_block, int *created)
 {
 	int tmp;
 	unsigned long *p;
@@ -589,31 +640,51 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
-		if (tmp == *p)
-			return result;
-		brelse(result);
-		goto repeat;
+		if (metadata) {
+			result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+			if (tmp == *p)
+				return result;
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
+		}
 	}
 	if (!create)
 		return NULL;
 	tmp = minix_new_block(inode->i_sb);
 	if (!tmp)
 		return NULL;
-	result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
-	if (*p) {
-		minix_free_block(inode->i_sb,tmp);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			brelse(result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, BLOCK_SIZE);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
 	*p = tmp;
+
 	inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
 	return result;
 }
 
 static struct buffer_head * V2_block_getblk(struct inode * inode,
-	struct buffer_head * bh, int nr, int create)
+	struct buffer_head * bh, int nr, int create,
+	int metadata, int *phys_block, int *created)
 {
 	int tmp;
 	unsigned long *p;
@@ -633,13 +704,19 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
-		if (tmp == *p) {
+		if (metadata) {
+			result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+			if (tmp == *p) {
+				brelse(bh);
+				return result;
+			}
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse(bh);
-			return result;
+			return NULL;
 		}
-		brelse(result);
-		goto repeat;
 	}
 	if (!create) {
 		brelse(bh);
@@ -650,60 +727,107 @@
 		brelse(bh);
 		return NULL;
 	}
-	result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
-	if (*p) {
-		minix_free_block(inode->i_sb,tmp);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			brelse(result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, BLOCK_SIZE);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			minix_free_block(inode->i_sb, tmp);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
+
 	*p = tmp;
 	mark_buffer_dirty(bh, 1);
 	brelse(bh);
 	return result;
 }
 
-static struct buffer_head * V2_minix_getblk(struct inode * inode, int block,
-					    int create)
+int V2_getblk_block(struct inode * inode, int block, int create, int *err, int *created)
 {
-	struct buffer_head * bh;
+	struct buffer_head * bh, *tmp;
+	int phys_block;
 
-	if (block<0) {
+	*err = -EIO;
+	if (block < 0) {
 		printk("minix_getblk: block<0");
-		return NULL;
+		return 0;
 	}
 	if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
 		printk("minix_getblk: block>big");
-		return NULL;
+		return 0;
+	}
+	*created = 0;
+	if (block < 7) {
+		tmp = V2_inode_getblk(inode, block, create,
+				      0, &phys_block, created);
+		goto out;
 	}
-	if (block < 7)
-		return V2_inode_getblk(inode,block,create);
 	block -= 7;
 	if (block < 256) {
-		bh = V2_inode_getblk(inode,7,create);
-		return V2_block_getblk(inode, bh, block, create);
+		bh = V2_inode_getblk(inode, 7, create, 1, NULL, NULL);
+		tmp = V2_block_getblk(inode, bh, block, create,
+				      0, &phys_block, created);
+		goto out;
 	}
 	block -= 256;
 	if (block < 256*256) {
-		bh = V2_inode_getblk(inode,8,create);
-		bh = V2_block_getblk(inode, bh, (block>>8) & 255, create);
-		return V2_block_getblk(inode, bh, block & 255, create);
+		bh = V2_inode_getblk(inode, 8, create, 1, NULL, NULL);
+		bh = V2_block_getblk(inode, bh, (block>>8) & 255, create,
+				     1, NULL, NULL);
+		tmp = V2_block_getblk(inode, bh, block & 255, create,
+				      0, &phys_block, created);
+		goto out;
 	}
 	block -= 256*256;
-	bh = V2_inode_getblk(inode,9,create);
-	bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create);
-	bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create);
-	return V2_block_getblk(inode, bh, block & 255, create);
+	bh = V2_inode_getblk(inode, 9, create, 1, NULL, NULL);
+	bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create, 1, NULL, NULL);
+	bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create, 1, NULL, NULL);
+	tmp = V2_block_getblk(inode, bh, block & 255, create, 0, &phys_block, created);
+
+out:
+	*err = 0;
+	return phys_block;
+}
+
+int minix_getblk_block (struct inode *inode, long block,
+			int create, int *err, int *created)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_getblk_block(inode, block, create, err, created);
+	else
+		return V2_getblk_block(inode, block, create, err, created);
 }
 
 /*
  * the global minix fs getblk function.
  */
-struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
+struct buffer_head *minix_getblk (struct inode *inode, int block, int create)
 {
-	if (INODE_VERSION(inode) == MINIX_V1)
-		return V1_minix_getblk(inode,block,create);
-	else
-		return V2_minix_getblk(inode,block,create);
+	struct buffer_head *tmp = NULL;
+	int phys_block;
+	int err, created;
+
+	phys_block = minix_getblk_block(inode, block, create, &err, &created);
+	if (phys_block) {
+		tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE);
+		if (created) {
+			memset(tmp->b_data, 0, BLOCK_SIZE);
+			mark_buffer_uptodate(tmp, 1);
+			mark_buffer_dirty(tmp, 1);
+		}
+	}
+	return tmp;
 }
 
 struct buffer_head * minix_bread(struct inode * inode, int block, int create)
diff -u --recursive --new-file v2.3.6/linux/fs/minix/truncate.c linux/fs/minix/truncate.c
--- v2.3.6/linux/fs/minix/truncate.c	Thu Nov 12 11:44:09 1998
+++ linux/fs/minix/truncate.c	Wed Jun 16 19:26:27 1999
@@ -32,6 +32,9 @@
  * general case (size = XXX). I hope.
  */
 
+#define DATA_BUFFER_USED(bh) \
+	((bh->b_count > 1) || buffer_locked(bh))
+
 /*
  * The functions for minix V1 fs truncation.
  */
@@ -52,7 +55,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || tmp != *p) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) {
 			retry = 1;
 			brelse(bh);
 			continue;
@@ -103,7 +106,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || tmp != *ind) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) {
 			retry = 1;
 			brelse(bh);
 			continue;
@@ -216,7 +219,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || tmp != *p) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) {
 			retry = 1;
 			brelse(bh);
 			continue;
@@ -267,7 +270,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || tmp != *ind) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) {
 			retry = 1;
 			brelse(bh);
 			continue;
diff -u --recursive --new-file v2.3.6/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.3.6/linux/fs/msdos/namei.c	Thu May 13 23:18:20 1999
+++ linux/fs/msdos/namei.c	Sat Jun 19 11:45:28 1999
@@ -633,7 +633,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,                   /* smap */
-	NULL,                   /* updatepage */
 	NULL,                   /* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.3.6/linux/fs/ncpfs/dir.c	Fri May 14 12:43:00 1999
+++ linux/fs/ncpfs/dir.c	Sat Jun 19 11:45:28 1999
@@ -98,7 +98,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL,			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.3.6/linux/fs/nfs/dir.c	Tue Jun  8 17:58:03 1999
+++ linux/fs/nfs/dir.c	Sat Jun 19 11:45:28 1999
@@ -78,13 +78,13 @@
 	nfs_rename,		/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	nfs_revalidate,		/* revalidate */
 };
 
@@ -118,6 +118,61 @@
 };
 static kmem_cache_t *nfs_cookie_cachep;
 
+/* This whole scheme relies on the fact that dirent cookies
+ * are monotonically increasing.
+ *
+ * Another invariant is that once we have a valid non-zero
+ * EOF marker cached, we also have the complete set of cookie
+ * table entries.
+ *
+ * We return the page offset assosciated with the page where
+ * cookie must be if it exists at all, however if we can not
+ * figure that out conclusively, we return < 0.
+ */
+static long __nfs_readdir_offset(struct inode *inode, __u32 cookie)
+{
+	struct nfs_cookie_table *p;
+	unsigned long ret = 0;
+
+	for(p = NFS_COOKIES(inode); p != NULL; p = p->next) {
+		int i;
+
+		for (i = 0; i < COOKIES_PER_CHUNK; i++) {
+			__u32 this_cookie = p->cookies[i];
+
+			/* End of known cookies, EOF is our only hope. */
+			if (!this_cookie)
+				goto check_eof;
+
+			/* Next cookie is larger, must be in previous page. */
+			if (this_cookie > cookie)
+				return ret;
+
+			ret += 1;
+
+			/* Exact cookie match, it must be in this page :-) */
+			if (this_cookie == cookie)
+				return ret;
+		}
+	}
+check_eof:
+	if (NFS_DIREOF(inode) != 0)
+		return ret;
+
+	return -1L;
+}
+
+static __inline__ long nfs_readdir_offset(struct inode *inode, __u32 cookie)
+{
+	/* Cookie zero is always at page offset zero.   Optimize the
+	 * other common case since most directories fit entirely
+	 * in one page.
+	 */
+	if (!cookie || (!NFS_COOKIES(inode) && NFS_DIREOF(inode)))
+		return 0;
+	return __nfs_readdir_offset(inode, cookie);
+}
+
 /* Since a cookie of zero is declared special by the NFS
  * protocol, we easily can tell if a cookie in an existing
  * table chunk is valid or not.
@@ -148,38 +203,7 @@
 	return ret;
 }
 
-/* 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.
- *
- * NOTE: Dirent information verification is done always by the
- *	 page-in of the RPC reply, nowhere else, this simplies
- *	 things substantially.
- */
 #define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
-static u32 find_midpoint(__u32 *p, u32 doff)
-{
-	u32 walk = doff & PAGE_MASK;
-
-	while(*p++ != 0) {
-		__u32 skip;
-
-		p++; /* skip fileid */
-
-		/* Skip len, name, and cookie. */
-		skip = NFS_NAMELEN_ALIGN(*p++);
-		p += (skip >> 2) + 1;
-		walk += skip + (4 * sizeof(__u32));
-		if (walk >= doff)
-			break;
-	}
-	return walk;
-}
-
 static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
 {
 	struct nfs_cookie_table **cpp;
@@ -211,48 +235,74 @@
 	return 0;
 }
 
-static struct page *try_to_get_dirent_page(struct file *, unsigned long, int);
+static struct page *try_to_get_dirent_page(struct file *, __u32, int);
 
 /* Recover from a revalidation flush.  The case here is that
  * the inode for the directory got invalidated somehow, and
  * all of our cached information is lost.  In order to get
  * a correct cookie for the current readdir request from the
  * user, we must (re-)fetch older readdir page cache entries.
+ *
+ * Returns < 0 if some error occurrs, else it is the page offset
+ * to fetch.
  */
-static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off)
+static long refetch_to_readdir_cookie(struct file *file, struct inode *inode)
 {
-	u32 cur_off, goal_off = off & PAGE_MASK;
+	struct page *page;
+	u32 goal_cookie = file->f_pos;
+	long cur_off, ret = -1L;
 
 again:
 	cur_off = 0;
-	while (cur_off < goal_off) {
-		struct page *page;
-
-		page = find_page(inode, cur_off);
+	for (;;) {
+		page = find_get_page(inode, cur_off);
 		if (page) {
-			if (PageLocked(page))
-				__wait_on_page(page);
-			if (!PageUptodate(page))
-				return -1;
+			if (!Page_Uptodate(page))
+				goto out_error;
 		} else {
-			page = try_to_get_dirent_page(file, cur_off, 0);
+			__u32 *cp = find_cookie(inode, cur_off);
+
+			if (!cp)
+				goto out_error;
+
+			page = try_to_get_dirent_page(file, *cp, 0);
 			if (!page) {
 				if (!cur_off)
-					return -1;
+					goto out_error;
 
 				/* Someone touched the dir on us. */
 				goto again;
 			}
-			page_cache_release(page);
 		}
+		page_cache_release(page);
+
+		if ((ret = nfs_readdir_offset(inode, goal_cookie)) >= 0)
+			goto out;
 
-		cur_off += PAGE_SIZE;
+		cur_off += 1;
 	}
+out:
+	return ret;
 
-	return 0;
+out_error:
+	if (page)
+		page_cache_release(page);
+	goto out;
 }
 
-static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok)
+/* 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.
+ *
+ * NOTE: Dirent information verification is done always by the
+ *	 page-in of the RPC reply, nowhere else, this simplies
+ *	 things substantially.
+ */
+static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int refetch_ok)
 {
 	struct nfs_readdirargs rd_args;
 	struct nfs_readdirres rd_res;
@@ -260,6 +310,7 @@
 	struct inode *inode = dentry->d_inode;
 	struct page *page, **hash;
 	unsigned long page_cache;
+	long offset;
 	__u32 *cookiep;
 
 	page = NULL;
@@ -267,27 +318,34 @@
 	if (!page_cache)
 		goto out;
 
-	while ((cookiep = find_cookie(inode, offset)) == NULL) {
+	if ((offset = nfs_readdir_offset(inode, cookie)) < 0) {
 		if (!refetch_ok ||
-		    refetch_to_readdir_off(file, inode, file->f_pos))
+		    (offset = refetch_to_readdir_cookie(file, inode)) < 0) {
+			page_cache_free(page_cache);
 			goto out;
+		}
+	}
+
+	cookiep = find_cookie(inode, offset);
+	if (!cookiep) {
+		/* Gross fatal error. */
+		page_cache_free(page_cache);
+		goto out;
 	}
 
 	hash = page_hash(inode, offset);
-	page = __find_page(inode, offset, *hash);
+repeat:
+	page = __find_lock_page(inode, offset, hash);
 	if (page) {
 		page_cache_free(page_cache);
-		goto out;
+		goto unlock_out;
 	}
 
 	page = page_cache_entry(page_cache);
-	atomic_inc(&page->count);
-	page->flags = ((page->flags &
-			~((1 << PG_uptodate) | (1 << PG_error))) |
-		       ((1 << PG_referenced) | (1 << PG_locked)));
-	page->offset = offset;
-	add_page_to_inode_queue(inode, page);
-	__add_page_to_hash_queue(page, hash);
+	if (add_to_page_cache_unique(page, inode, offset, hash)) {
+		page_cache_release(page);
+		goto repeat;
+	}
 
 	rd_args.fh = NFS_FH(dentry);
 	rd_res.buffer = (char *)page_cache;
@@ -303,48 +361,50 @@
 	} while(rd_res.bufsiz > 0);
 
 	if (rd_res.bufsiz < 0)
-		NFS_DIREOF(inode) =
-			(offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz);
+		NFS_DIREOF(inode) = rd_res.cookie;
 	else if (create_cookie(rd_res.cookie, offset, inode))
 		goto error;
 
-	set_bit(PG_uptodate, &page->flags);
+	SetPageUptodate(page);
 unlock_out:
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	UnlockPage(page);
 out:
 	return page;
 
 error:
-	set_bit(PG_error, &page->flags);
+	SetPageError(page);
 	goto unlock_out;
 }
 
-static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff,
+/* Seek up to dirent assosciated with the passed in cookie,
+ * then fill in dirents found.  Return the last cookie
+ * actually given to the user, to update the file position.
+ */
+static __inline__ u32 nfs_do_filldir(__u32 *p, u32 cookie,
 				     void *dirent, filldir_t filldir)
 {
 	u32 end;
 
-	if (doff & ~PAGE_CACHE_MASK) {
-		doff = find_midpoint(p, doff);
-		p += (doff & ~PAGE_CACHE_MASK) >> 2;
-	}
 	while((end = *p++) != 0) {
-		__u32 fileid = *p++;
-		__u32 len = *p++;
-		__u32 skip = NFS_NAMELEN_ALIGN(len);
-		char *name = (char *) p;
-
-		/* Skip the cookie. */
-		p = ((__u32 *) (name + skip)) + 1;
-		if (filldir(dirent, name, len, doff, fileid) < 0)
-			goto out;
-		doff += (skip + (4 * sizeof(__u32)));
+		__u32 fileid, len, skip, this_cookie;
+		char *name;
+
+		fileid = *p++;
+		len = *p++;
+		name = (char *) p;
+		skip = NFS_NAMELEN_ALIGN(len);
+		p += (skip >> 2);
+		this_cookie = *p++;
+
+		if (this_cookie < cookie)
+			continue;
+
+		cookie = this_cookie;
+		if (filldir(dirent, name, len, cookie, fileid) < 0)
+			break;
 	}
-	if (!*p)
-		doff = PAGE_CACHE_ALIGN(doff);
-out:
-	return doff;
+
+	return cookie;
 }
 
 /* The file offset position is represented in pure bytes, to
@@ -359,7 +419,7 @@
 	struct dentry *dentry = filp->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	struct page *page, **hash;
-	unsigned long offset;
+	long offset;
 	int res;
 
 	res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
@@ -369,14 +429,14 @@
 	if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
 		return 0;
 
-	offset = filp->f_pos >> PAGE_CACHE_SHIFT;
+	if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0)
+		goto no_dirent_page;
+
 	hash = page_hash(inode, offset);
-	page = __find_page(inode, offset, *hash);
+	page = __find_get_page(inode, offset, hash);
 	if (!page)
 		goto no_dirent_page;
-	if (PageLocked(page))
-		goto dirent_locked_wait;
-	if (!PageUptodate(page))
+	if (!Page_Uptodate(page))
 		goto dirent_read_error;
 success:
 	filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
@@ -385,13 +445,11 @@
 	return 0;
 
 no_dirent_page:
-	page = try_to_get_dirent_page(filp, offset, 1);
+	page = try_to_get_dirent_page(filp, filp->f_pos, 1);
 	if (!page)
 		goto no_page;
 
-dirent_locked_wait:
-	wait_on_page(page);
-	if (PageUptodate(page))
+	if (Page_Uptodate(page))
 		goto success;
 dirent_read_error:
 	page_cache_release(page);
@@ -399,20 +457,39 @@
 	return -EIO;
 }
 
-/* Invalidate directory cookie caches and EOF marker
- * for an inode.
+/* Flush directory cookie and EOF caches for an inode.
+ * So we don't thrash allocating/freeing cookie tables,
+ * we keep the cookies around until the inode is
+ * deleted/reused.
+ */
+__inline__ void nfs_flush_dircache(struct inode *inode)
+{
+	struct nfs_cookie_table *p = NFS_COOKIES(inode);
+
+	while (p != NULL) {
+		int i;
+
+		for(i = 0; i < COOKIES_PER_CHUNK; i++)
+			p->cookies[i] = 0;
+
+		p = p->next;
+	}
+	NFS_DIREOF(inode) = 0;
+}
+
+/* Free up directory cache state, this happens when
+ * nfs_delete_inode is called on an NFS directory.
  */
-__inline__ void nfs_invalidate_dircache(struct inode *inode)
+void nfs_free_dircache(struct inode *inode)
 {
 	struct nfs_cookie_table *p = NFS_COOKIES(inode);
 
-	if (p != NULL) {
-		NFS_COOKIES(inode) = NULL;
-		do {	struct nfs_cookie_table *next = p->next;
-			kmem_cache_free(nfs_cookie_cachep, p);
-			p = next;
-		} while (p != NULL);
+	while (p != NULL) {
+		struct nfs_cookie_table *next = p->next;
+		kmem_cache_free(nfs_cookie_cachep, p);
+		p = next;
 	}
+	NFS_COOKIES(inode) = NULL;
 	NFS_DIREOF(inode) = 0;
 }
 
@@ -538,11 +615,11 @@
 	/* Purge readdir caches. */
 	if (dentry->d_parent->d_inode) {
 		invalidate_inode_pages(dentry->d_parent->d_inode);
-		nfs_invalidate_dircache(dentry->d_parent->d_inode);
+		nfs_flush_dircache(dentry->d_parent->d_inode);
 	}
 	if (inode && S_ISDIR(inode->i_mode)) {
 		invalidate_inode_pages(inode);
-		nfs_invalidate_dircache(inode);
+		nfs_flush_dircache(inode);
 	}
 	return 0;
 }
@@ -739,7 +816,7 @@
 	 * Invalidate the dir cache before the operation to avoid a race.
 	 */
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 			dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
@@ -769,7 +846,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
@@ -804,7 +881,7 @@
 	 */
 	d_drop(dentry);
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	return error;
@@ -825,7 +902,7 @@
 #endif
 
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name);
 
@@ -953,7 +1030,7 @@
 	} while(sdentry->d_inode != NULL); /* need negative lookup */
 
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_rename(NFS_SERVER(dir),
 				NFS_FH(dentry->d_parent), dentry->d_name.name,
 				NFS_FH(dentry->d_parent), silly);
@@ -1023,7 +1100,7 @@
 		d_delete(dentry);
 	}
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name);
 	/*
@@ -1090,7 +1167,7 @@
 	 */
 	d_drop(dentry);
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_dircache(dir);
 	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, symname, &sattr);
 	if (!error) {
@@ -1121,7 +1198,7 @@
 	 */
 	d_drop(dentry);
 	invalidate_inode_pages(dir);
-	nfs_invalidate_dircache(dir);
+	nfs_flush_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) {
@@ -1267,9 +1344,9 @@
 	}
 
 	invalidate_inode_pages(new_dir);
-	nfs_invalidate_dircache(new_dir);
+	nfs_flush_dircache(new_dir);
 	invalidate_inode_pages(old_dir);
-	nfs_invalidate_dircache(old_dir);
+	nfs_flush_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);
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.3.6/linux/fs/nfs/file.c	Wed Jun  2 13:46:59 1999
+++ linux/fs/nfs/file.c	Sat Jun 19 11:45:28 1999
@@ -26,6 +26,7 @@
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
 #include <linux/lockd/bind.h>
+#include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 #include <asm/segment.h>
@@ -70,13 +71,13 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	nfs_readpage,		/* readpage */
 	nfs_writepage,		/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	nfs_revalidate,		/* revalidate */
 };
 
@@ -172,8 +173,11 @@
 
 	bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
 	status = -EFAULT;
-	if (bytes)
+	if (bytes) {
+		lock_kernel();
 		status = nfs_updatepage(file, page, offset, bytes);
+		unlock_kernel();
+	}
 	return status;
 }
 
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.3.6/linux/fs/nfs/inode.c	Tue Jun  8 17:58:03 1999
+++ linux/fs/nfs/inode.c	Wed Jun 16 19:26:27 1999
@@ -99,23 +99,28 @@
 	int failed;
 
 	dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
-	/*
-	 * Flush out any pending write requests ...
-	 */
-	if (NFS_WRITEBACK(inode) != NULL) {
-		unsigned long timeout = jiffies + 5*HZ;
+
+	if (S_ISDIR(inode->i_mode)) {
+		nfs_free_dircache(inode);
+	} else {
+		/*
+		 * 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);
+			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");
 		}
-		current->state = TASK_RUNNING;
-		if (NFS_WRITEBACK(inode) != NULL)
-			printk("NFS: Arghhh, stuck RPC requests!\n");
 	}
 
 	failed = nfs_check_failed_request(inode);
@@ -433,7 +438,7 @@
 
 	invalidate_inode_pages(inode);
 	if (S_ISDIR(inode->i_mode))
-		nfs_invalidate_dircache(inode);
+		nfs_flush_dircache(inode);
 }
 
 /*
@@ -477,8 +482,6 @@
 		inode->i_size  = fattr->size;
 		inode->i_mtime = fattr->mtime.seconds;
 		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
-		NFS_COOKIES(inode) = NULL;
-		NFS_WRITEBACK(inode) = NULL;
 	}
 	nfs_refresh_inode(inode, fattr);
 }
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.3.6/linux/fs/nfs/read.c	Mon Jun  7 13:25:49 1999
+++ linux/fs/nfs/read.c	Sat Jun 19 11:38:49 1999
@@ -26,6 +26,7 @@
 #include <linux/pagemap.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/smp_lock.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -77,7 +78,6 @@
 	int		flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
 
 	dprintk("NFS: nfs_readpage_sync(%p)\n", page);
-	clear_bit(PG_error, &page->flags);
 
 	do {
 		if (count < rsize)
@@ -111,16 +111,14 @@
 	} while (count);
 
 	memset(buffer, 0, count);
-	set_bit(PG_uptodate, &page->flags);
+	SetPageUptodate(page);
 	result = 0;
 
 io_error:
+	UnlockPage(page);
 	/* 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);
 	return result;
 }
 
@@ -146,17 +144,15 @@
 			memset((char *) address + result, 0, PAGE_SIZE - result);
 		}
 		nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
-		set_bit(PG_uptodate, &page->flags);
+		SetPageUptodate(page);
 		succ++;
 	} else {
-		set_bit(PG_error, &page->flags);
+		SetPageError(page);
 		fail++;
 		dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
 	}
-	/* N.B. Use nfs_unlock_page here? */
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-
+	page->owner = (int)current; // HACK, FIXME, will go away.
+	UnlockPage(page);
 	free_page(address);
 
 	rpc_release_task(task);
@@ -227,10 +223,10 @@
 	struct inode *inode = dentry->d_inode;
 	int		error;
 
+	lock_kernel();
 	dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
 		page, PAGE_SIZE, page->offset);
-	atomic_inc(&page->count);
-	set_bit(PG_locked, &page->flags);
+	get_page(page);
 
 	/*
 	 * Try to flush any pending writes to the file..
@@ -256,10 +252,10 @@
 	goto out_free;
 
 out_error:
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	UnlockPage(page);
 out_free:
 	free_page(page_address(page));
 out:
+	unlock_kernel();
 	return error;
 }
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.3.6/linux/fs/nfs/symlink.c	Tue Jun  8 22:11:58 1999
+++ linux/fs/nfs/symlink.c	Sat Jun 19 11:45:29 1999
@@ -43,11 +43,14 @@
 	NULL,			/* rename */
 	nfs_readlink,		/* readlink */
 	nfs_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	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
@@ -65,20 +68,18 @@
 		goto out;
 
 	hash = page_hash(inode, 0);
-	page = __find_page(inode, 0, *hash);
+repeat:
+	page = __find_lock_page(inode, 0, hash);
 	if (page) {
 		page_cache_free(page_cache);
-		goto out;
+		goto unlock_out;
 	}
 
 	page = page_cache_entry(page_cache);
-	atomic_inc(&page->count);
-	page->flags = ((page->flags &
-			~((1 << PG_uptodate) | (1 << PG_error))) |
-		       ((1 << PG_referenced) | (1 << PG_locked)));
-	page->offset = 0;
-	add_page_to_inode_queue(inode, page);
-	__add_page_to_hash_queue(page, hash);
+	if (add_to_page_cache_unique(page, inode, 0, hash)) {
+		page_cache_release(page);
+		goto repeat;
+	}
 
 	/* We place the length at the beginning of the page,
 	 * in host byte order, followed by the string.  The
@@ -89,32 +90,28 @@
 	if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK,
 		     &rl_args, NULL, 0) < 0)
 		goto error;
-	set_bit(PG_uptodate, &page->flags);
+	SetPageUptodate(page);
 unlock_out:
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	UnlockPage(page);
 out:
 	return page;
 
 error:
-	set_bit(PG_error, &page->flags);
+	SetPageError(page);
 	goto unlock_out;
 }
 
 static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	struct inode *inode = dentry->d_inode;
-	struct page *page, **hash;
+	struct page *page;
 	u32 *p, len;
 
 	/* Caller revalidated the directory inode already. */
-	hash = page_hash(inode, 0);
-	page = __find_page(inode, 0, *hash);
+	page = find_get_page(inode, 0);
 	if (!page)
 		goto no_readlink_page;
-	if (PageLocked(page))
-		goto readlink_locked_wait;
-	if (!PageUptodate(page))
+	if (!Page_Uptodate(page))
 		goto readlink_read_error;
 success:
 	p = (u32 *) page_address(page);
@@ -129,9 +126,7 @@
 	page = try_to_get_symlink_page(dentry, inode);
 	if (!page)
 		goto no_page;
-readlink_locked_wait:
-	wait_on_page(page);
-	if (PageUptodate(page))
+	if (Page_Uptodate(page))
 		goto success;
 readlink_read_error:
 	page_cache_release(page);
@@ -144,17 +139,14 @@
 {
 	struct dentry *result;
 	struct inode *inode = dentry->d_inode;
-	struct page *page, **hash;
+	struct page *page;
 	u32 *p;
 
 	/* Caller revalidated the directory inode already. */
-	hash = page_hash(inode, 0);
-	page = __find_page(inode, 0, *hash);
+	page = find_get_page(inode, 0);
 	if (!page)
 		goto no_followlink_page;
-	if (PageLocked(page))
-		goto followlink_locked_wait;
-	if (!PageUptodate(page))
+	if (!Page_Uptodate(page))
 		goto followlink_read_error;
 success:
 	p = (u32 *) page_address(page);
@@ -166,9 +158,7 @@
 	page = try_to_get_symlink_page(dentry, inode);
 	if (!page)
 		goto no_page;
-followlink_locked_wait:
-	wait_on_page(page);
-	if (PageUptodate(page))
+	if (Page_Uptodate(page))
 		goto success;
 followlink_read_error:
 	page_cache_release(page);
diff -u --recursive --new-file v2.3.6/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.3.6/linux/fs/nfs/write.c	Tue Jun  8 17:58:03 1999
+++ linux/fs/nfs/write.c	Sat Jun 19 11:38:49 1999
@@ -55,6 +55,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
 #include <asm/uaccess.h>
+#include <linux/smp_lock.h>
 
 #define NFS_PARANOIA 1
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
@@ -93,6 +94,7 @@
 	u8		*buffer;
 	struct nfs_fattr fattr;
 
+	lock_kernel();
 	dprintk("NFS:      nfs_writepage_sync(%s/%s %d@%ld)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		count, page->offset + offset);
@@ -110,7 +112,7 @@
 
 		if (result < 0) {
 			/* Must mark the page invalid after I/O error */
-			clear_bit(PG_uptodate, &page->flags);
+			ClearPageUptodate(page);
 			goto io_error;
 		}
 		if (result != wsize)
@@ -153,6 +155,7 @@
 				inode->i_ino, fattr.fileid);
 	}
 
+	unlock_kernel();
 	return written? written : result;
 }
 
@@ -463,7 +466,7 @@
 	 * 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);
+	get_page(page);
 	file->f_count++;
 
 	/* Schedule request */
@@ -471,7 +474,7 @@
 
 updated:
 	if (req->wb_bytes == PAGE_SIZE)
-		set_bit(PG_uptodate, &page->flags);
+		SetPageUptodate(page);
 
 	retval = count;
 	if (synchronous) {
@@ -486,7 +489,7 @@
 		}
 
 		if (retval < 0)
-			clear_bit(PG_uptodate, &page->flags);
+			ClearPageUptodate(page);
 	}
 
 	free_write_request(req);
@@ -682,7 +685,7 @@
 	rpc_release_task(task);
 
 	if (WB_INVALIDATE(req))
-		clear_bit(PG_uptodate, &page->flags);
+		ClearPageUptodate(page);
 
 	__free_page(page);
 	remove_write_request(&NFS_WRITEBACK(inode), req);
diff -u --recursive --new-file v2.3.6/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.3.6/linux/fs/ntfs/fs.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/ntfs/fs.c	Sat Jun 19 11:45:28 1999
@@ -445,7 +445,6 @@
 	NULL, /* truncate */
 	NULL, /* permission */
 	NULL, /* smap */
-	NULL, /* updatepage */
 	NULL, /* revalidate */
 };
 
@@ -628,7 +627,6 @@
 	NULL, /* truncate */
 	NULL, /* permission */
 	NULL, /* smap */
-	NULL, /* updatepage */
 	NULL, /* revalidate */
 };
 
@@ -677,7 +675,6 @@
 	NULL, /* truncate */
 	NULL, /* permission */
 	NULL, /* smap */
-	NULL, /* updatepage */
 	NULL, /* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/pipe.c linux/fs/pipe.c
--- v2.3.6/linux/fs/pipe.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/pipe.c	Sat Jun 19 11:45:28 1999
@@ -7,6 +7,7 @@
 #include <linux/mm.h>
 #include <linux/file.h>
 #include <linux/poll.h>
+#include <linux/malloc.h>
 
 #include <asm/uaccess.h>
 
@@ -101,9 +102,7 @@
 		free = count;
 	else
 		free = 1; /* can't do it atomically, wait for any free space */
-	up(&inode->i_sem);
-	if (down_interruptible(&inode->i_atomic_write)) {
-		down(&inode->i_sem);
+	if (down_interruptible(&inode->i_sem)) {
 		return -ERESTARTSYS;
 	}
 	while (count>0) {
@@ -144,8 +143,7 @@
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(inode);
 errout:
-	up(&inode->i_atomic_write);
-	down(&inode->i_sem);
+	up(&inode->i_sem);
 	return written ? written : err;
 }
 
@@ -249,8 +247,11 @@
 static int pipe_release(struct inode * inode)
 {
 	if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
-		free_page((unsigned long) PIPE_BASE(*inode));
-		PIPE_BASE(*inode) = NULL;
+		struct pipe_inode_info *info = inode->i_pipe;
+		inode->i_pipe = NULL;
+		free_page((unsigned long) info->base);
+		kfree(info);
+		return 0;
 	}
 	wake_up_interruptible(&PIPE_WAIT(*inode));
 	return 0;
@@ -404,36 +405,48 @@
 {
 	extern struct inode_operations pipe_inode_operations;
 	struct inode *inode = get_empty_inode();
+	unsigned long page;
 
-	if (inode) {
-		unsigned long page = __get_free_page(GFP_USER);
+	if (!inode)
+		goto fail_inode;
 
-		if (!page) {
-			iput(inode);
-			inode = NULL;
-		} else {
-			PIPE_BASE(*inode) = (char *) page;
-			inode->i_op = &pipe_inode_operations;
-			init_waitqueue_head(&PIPE_WAIT(*inode));
-			PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
-			PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
-			PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
-			PIPE_LOCK(*inode) = 0;
-			/*
-			 * Mark the inode dirty from the very beginning,
-			 * that way it will never be moved to the dirty
-			 * list because "mark_inode_dirty()" will think
-			 * that it already _is_ on the dirty list.
-			 */
-			inode->i_state = I_DIRTY;
-			inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
-			inode->i_uid = current->fsuid;
-			inode->i_gid = current->fsgid;
-			inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-			inode->i_blksize = PAGE_SIZE;
-		}
-	}
+	page = __get_free_page(GFP_USER);
+
+	if (!page)
+		goto fail_iput;
+
+	/* XXX */
+	inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
+	if (!inode->i_pipe)
+		goto fail_page;
+
+	PIPE_BASE(*inode) = (char *) page;
+	inode->i_op = &pipe_inode_operations;
+	init_waitqueue_head(&PIPE_WAIT(*inode));
+	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
+	PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
+	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+	PIPE_LOCK(*inode) = 0;
+	/*
+	 * Mark the inode dirty from the very beginning,
+	 * that way it will never be moved to the dirty
+	 * list because "mark_inode_dirty()" will think
+	 * that it already _is_ on the dirty list.
+	 */
+	inode->i_state = I_DIRTY;
+	inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
+	inode->i_uid = current->fsuid;
+	inode->i_gid = current->fsgid;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_blksize = PAGE_SIZE;
 	return inode;
+
+fail_page:
+	free_page(page);
+fail_iput:
+	iput(inode);
+fail_inode:
+	return NULL;
 }
 
 struct inode_operations pipe_inode_operations = {
@@ -448,11 +461,14 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 int do_pipe(int *fd)
@@ -513,6 +529,8 @@
 	put_unused_fd(i);
 close_f12_inode:
 	free_page((unsigned long) PIPE_BASE(*inode));
+	kfree(inode->i_pipe);
+	inode->i_pipe = NULL;
 	iput(inode);
 close_f12:
 	put_filp(f2);
diff -u --recursive --new-file v2.3.6/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.3.6/linux/fs/proc/array.c	Tue May 11 16:30:45 1999
+++ linux/fs/proc/array.c	Sat Jun 19 11:45:28 1999
@@ -348,7 +348,7 @@
 	len = sprintf(buffer, "        total:    used:    free:  shared: buffers:  cached:\n"
 		"Mem:  %8lu %8lu %8lu %8lu %8lu %8lu\n"
 		"Swap: %8lu %8lu %8lu\n",
-		i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE,
+		i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, atomic_read(&page_cache_size)*PAGE_SIZE,
 		i.totalswap, i.totalswap-i.freeswap, i.freeswap);
 	/*
 	 * Tagged format, for easy grepping and expansion. The above will go away
@@ -359,14 +359,14 @@
 		"MemFree:   %8lu kB\n"
 		"MemShared: %8lu kB\n"
 		"Buffers:   %8lu kB\n"
-		"Cached:    %8lu kB\n"
+		"Cached:    %8u kB\n"
 		"SwapTotal: %8lu kB\n"
 		"SwapFree:  %8lu kB\n",
 		i.totalram >> 10,
 		i.freeram >> 10,
 		i.sharedram >> 10,
 		i.bufferram >> 10,
-		page_cache_size << (PAGE_SHIFT - 10),
+		atomic_read(&page_cache_size) << (PAGE_SHIFT - 10),
 		i.totalswap >> 10,
 		i.freeswap >> 10);
 }
@@ -975,7 +975,7 @@
 			++*dirty;
 		if (MAP_NR(pte_page(page)) >= max_mapnr)
 			continue;
-		if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) > 1)
+		if (page_count(mem_map + MAP_NR(pte_page(page))) > 1)
 			++*shared;
 	} while (address < end);
 }
@@ -1326,6 +1326,9 @@
 
 		case PROC_IOPORTS:
 			return get_ioport_list(page);
+
+		case PROC_MEMORY:
+			return get_mem_list(page);
 #ifdef CONFIG_BLK_DEV_MD
 	        case PROC_MD:
 			return get_md_status(page);
@@ -1516,11 +1519,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static ssize_t arraylong_read(struct file * file, char * buf,
@@ -1564,9 +1570,12 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/proc/base.c linux/fs/proc/base.c
--- v2.3.6/linux/fs/proc/base.c	Mon Aug 24 13:02:43 1998
+++ linux/fs/proc/base.c	Sat Jun 19 11:45:28 1999
@@ -45,11 +45,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
diff -u --recursive --new-file v2.3.6/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v2.3.6/linux/fs/proc/fd.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/proc/fd.c	Sat Jun 19 11:45:28 1999
@@ -51,11 +51,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	proc_permission		/* permission */
+	proc_permission,	/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
diff -u --recursive --new-file v2.3.6/linux/fs/proc/generic.c linux/fs/proc/generic.c
--- v2.3.6/linux/fs/proc/generic.c	Mon Aug 24 13:02:44 1998
+++ linux/fs/proc/generic.c	Sat Jun 19 11:45:28 1999
@@ -51,20 +51,23 @@
 	&proc_file_operations,  /* default proc file-ops */
 	NULL,		/* create	*/
 	NULL,		/* lookup	*/
-    NULL,		/* link		*/
-    NULL,		/* unlink	*/
-    NULL,		/* symlink	*/
-    NULL,		/* mkdir	*/
-    NULL,	    /* rmdir	   */
-    NULL,	    /* mknod	   */
-    NULL,	    /* rename	   */
-    NULL,	    /* readlink	   */
-    NULL,	    /* follow_link */
-    NULL,	    /* readpage	   */
-    NULL,	    /* writepage   */
-    NULL,	    /* bmap	   */
-    NULL,	    /* truncate	   */
-    NULL	    /* permission  */
+	NULL,		/* link		*/
+	NULL,		/* unlink	*/
+	NULL,		/* symlink	*/
+	NULL,		/* mkdir	*/
+	NULL,		/* rmdir	*/
+	NULL,		/* mknod	*/
+	NULL,		/* rename	*/
+	NULL,		/* readlink	*/
+	NULL,		/* follow_link 	*/
+	NULL,		/* bmap 	*/
+	NULL,		/* readpage 	*/
+	NULL,		/* writepage 	*/
+	NULL,		/* flushpage 	*/
+	NULL,		/* truncate 	*/
+	NULL,		/* permission 	*/
+	NULL,		/* smap 	*/
+	NULL		/* revalidate 	*/
 };
 
 /*
@@ -83,11 +86,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 
diff -u --recursive --new-file v2.3.6/linux/fs/proc/kmsg.c linux/fs/proc/kmsg.c
--- v2.3.6/linux/fs/proc/kmsg.c	Tue May 11 14:37:40 1999
+++ linux/fs/proc/kmsg.c	Sat Jun 19 11:45:28 1999
@@ -72,9 +72,12 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.3.6/linux/fs/proc/link.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/proc/link.c	Sat Jun 19 11:45:28 1999
@@ -49,11 +49,14 @@
 	NULL,			/* rename */
 	proc_readlink,		/* readlink */
 	proc_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	proc_permission		/* permission */
+	proc_permission,	/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static struct dentry * proc_follow_link(struct dentry *dentry,
diff -u --recursive --new-file v2.3.6/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v2.3.6/linux/fs/proc/mem.c	Wed Sep 23 15:24:37 1998
+++ linux/fs/proc/mem.c	Sat Jun 19 11:45:28 1999
@@ -298,7 +298,7 @@
 		set_pte(dest_table, *src_table);
 		mapnr = MAP_NR(pte_page(*src_table));
 		if (mapnr < max_mapnr)
-			atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
+			get_page(mem_map + MAP_NR(pte_page(*src_table)));
 
 		stmp += PAGE_SIZE;
 		dtmp += PAGE_SIZE;
@@ -336,9 +336,12 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	proc_permission		/* permission */
+	proc_permission,	/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/proc/net.c linux/fs/proc/net.c
--- v2.3.6/linux/fs/proc/net.c	Mon Aug 24 13:14:09 1998
+++ linux/fs/proc/net.c	Sat Jun 19 11:45:28 1999
@@ -113,9 +113,12 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/proc/omirr.c linux/fs/proc/omirr.c
--- v2.3.6/linux/fs/proc/omirr.c	Tue May 11 23:07:08 1999
+++ linux/fs/proc/omirr.c	Sat Jun 19 11:45:28 1999
@@ -277,22 +277,24 @@
 };
 
 struct inode_operations proc_omirr_inode_operations = {
-    &omirr_operations,
-    NULL, /* create */
-    NULL, /* lookup */
-    NULL, /* link */
-    NULL, /* unlink */
-    NULL, /* symlink */
-    NULL, /* mkdir */
-    NULL, /* rmdir */
-    NULL, /* mknod */
-    NULL, /* rename */
-    NULL, /* readlink */
-    NULL, /* follow_link */
-    NULL, /* readpage */
-    NULL, /* writepage */
-    NULL, /* bmap */
-    NULL, /* truncate */
-    NULL, /* permission */
-    NULL  /* smap */
+	&omirr_operations,
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* flushpage */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c
--- v2.3.6/linux/fs/proc/proc_devtree.c	Sat Sep 19 13:43:36 1998
+++ linux/fs/proc/proc_devtree.c	Sat Jun 19 11:45:29 1999
@@ -57,12 +57,14 @@
 	NULL,			/* rename */
 	devtree_readlink,	/* readlink */
 	devtree_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
 	NULL,			/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static struct dentry *devtree_follow_link(struct dentry *dentry,
diff -u --recursive --new-file v2.3.6/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.3.6/linux/fs/proc/root.c	Mon Jun  7 12:20:50 1999
+++ linux/fs/proc/root.c	Sat Jun 19 11:45:29 1999
@@ -71,11 +71,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
@@ -94,11 +97,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
@@ -136,11 +142,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 /*
@@ -293,11 +302,14 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 struct proc_dir_entry proc_openprom = {
@@ -478,11 +490,14 @@
 	NULL,			/* rename */
 	proc_self_readlink,	/* readlink */
 	proc_self_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static struct inode_operations proc_link_inode_operations = {
@@ -498,11 +513,14 @@
 	NULL,			/* rename */
 	proc_readlink,		/* readlink */
 	proc_follow_link,	/* follow_link */
+	NULL,			/* bmap */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 static struct proc_dir_entry proc_root_loadavg = {
@@ -621,6 +639,11 @@
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_array_inode_operations
 };
+static struct proc_dir_entry proc_root_memory = {
+	PROC_MEMORY, 6, "memory",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
+};
 static struct proc_dir_entry proc_root_cmdline = {
 	PROC_CMDLINE, 7, "cmdline",
 	S_IFREG | S_IRUGO, 1, 0, 0,
@@ -709,6 +732,7 @@
 	proc_register(&proc_root, &proc_root_fs);
 	proc_register(&proc_root, &proc_root_dma);
 	proc_register(&proc_root, &proc_root_ioports);
+	proc_register(&proc_root, &proc_root_memory);
 	proc_register(&proc_root, &proc_root_cmdline);
 #ifdef CONFIG_RTC
 	proc_register(&proc_root, &proc_root_rtc);
diff -u --recursive --new-file v2.3.6/linux/fs/proc/scsi.c linux/fs/proc/scsi.c
--- v2.3.6/linux/fs/proc/scsi.c	Mon Aug 24 13:14:10 1998
+++ linux/fs/proc/scsi.c	Sat Jun 19 11:45:29 1999
@@ -59,23 +59,26 @@
  * proc directories can do almost nothing..
  */
 struct inode_operations proc_scsi_inode_operations = {
-    &proc_scsi_operations,  /* default scsi directory file-ops */
-    NULL,	    /* create	   */
-    proc_lookup,    /* lookup	   */
-    NULL,	    /* link	   */
-    NULL,	    /* unlink	   */
-    NULL,	    /* symlink	   */
-    NULL,	    /* mkdir	   */
-    NULL,	    /* rmdir	   */
-    NULL,	    /* mknod	   */
-    NULL,	    /* rename	   */
-    NULL,	    /* readlink	   */
-    NULL,	    /* follow_link */
-    NULL,	    /* readpage	   */
-    NULL,	    /* writepage   */
-    NULL,	    /* bmap	   */
-    NULL,	    /* truncate	   */
-    NULL	    /* permission  */
+&proc_scsi_operations,  /* default scsi directory file-ops */
+	NULL,			/* create */
+	proc_lookup,		/* lookup */
+	NULL,	    		/* link */
+	NULL,	    		/* unlink */
+	NULL,	    		/* symlink */
+	NULL,	    		/* mkdir */
+	NULL,	    		/* rmdir */
+	NULL,	    		/* mknod */
+	NULL,	    		/* rename */
+	NULL,	    		/* readlink */
+	NULL,	    		/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* flushpage */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
 int get_not_present_info(char *buffer, char **start, off_t offset, int length)
diff -u --recursive --new-file v2.3.6/linux/fs/proc/sysvipc.c linux/fs/proc/sysvipc.c
--- v2.3.6/linux/fs/proc/sysvipc.c	Mon Jun  7 12:20:50 1999
+++ linux/fs/proc/sysvipc.c	Sat Jun 19 11:45:29 1999
@@ -118,21 +118,24 @@
  * proc directories can do almost nothing..
  */
 struct inode_operations proc_sysvipc_inode_operations = {
-	&proc_sysvipc_operations,	/* default net file-ops */
-	NULL,				/* create */
-	NULL,				/* lookup */
-	NULL,				/* link */
-	NULL,				/* unlink */
-	NULL,				/* symlink */
-	NULL,				/* mkdir */
-	NULL,				/* rmdir */
-	NULL,				/* mknod */
-	NULL,				/* rename */
-	NULL,				/* readlink */
-	NULL,				/* follow_link */
-	NULL,				/* readpage */
-	NULL,				/* writepage */
-	NULL,				/* bmap */
-	NULL,				/* truncate */
-	NULL				/* permission */
+	&proc_sysvipc_operations, /* default net file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* flushpage */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/read_write.c linux/fs/read_write.c
--- v2.3.6/linux/fs/read_write.c	Sun Dec 27 10:52:09 1998
+++ linux/fs/read_write.c	Wed Jun 16 19:26:27 1999
@@ -166,9 +166,7 @@
 	if (!file->f_op || !(write = file->f_op->write))
 		goto out;
 
-	down(&inode->i_sem);
 	ret = write(file, buf, count, &file->f_pos);
-	up(&inode->i_sem);
 out:
 	fput(file);
 bad_file:
@@ -304,9 +302,7 @@
 	if (!file)
 		goto bad_file;
 	if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) {
-		down(&file->f_dentry->d_inode->i_sem);
 		ret = do_readv_writev(VERIFY_READ, file, vector, count);
-		up(&file->f_dentry->d_inode->i_sem);
 	}
 	fput(file);
 
@@ -376,10 +372,7 @@
 	if (pos < 0)
 		goto out;
 
-	down(&file->f_dentry->d_inode->i_sem);
 	ret = write(file, buf, count, &pos);
-	up(&file->f_dentry->d_inode->i_sem);
-
 out:
 	fput(file);
 bad_file:
diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v2.3.6/linux/fs/smbfs/dir.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/smbfs/dir.c	Sat Jun 19 11:45:28 1999
@@ -65,7 +65,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	smb_revalidate_inode,	/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- v2.3.6/linux/fs/smbfs/file.c	Wed Jun  2 13:46:59 1999
+++ linux/fs/smbfs/file.c	Sat Jun 19 11:45:28 1999
@@ -14,6 +14,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
+#include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -271,8 +272,11 @@
 
 	bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
 	status = -EFAULT;
-	if (bytes)
+	if (bytes) {
+		lock_kernel();
 		status = smb_updatepage(file, page, offset, bytes);
+		unlock_kernel();
+	}
 	return status;
 }
 
@@ -406,6 +410,5 @@
 	NULL,			/* truncate */
 	smb_file_permission,	/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	smb_revalidate_inode,	/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.3.6/linux/fs/smbfs/inode.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/smbfs/inode.c	Wed Jun 16 19:26:27 1999
@@ -376,9 +376,6 @@
 	*mnt = *((struct smb_mount_data *) raw_data);
 	/* ** temp ** pass config flags in file mode */
 	mnt->version = (mnt->file_mode >> 9);
-#ifdef CONFIG_SMB_WIN95
-	mnt->version |= SMB_FIX_WIN95;
-#endif
 	mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
 	mnt->file_mode |= S_IFREG;
 	mnt->dir_mode  &= (S_IRWXU | S_IRWXG | S_IRWXO);
@@ -387,8 +384,6 @@
 	/*
 	 * Display the enabled options
 	 */
-	if (mnt->version & SMB_FIX_WIN95)
-		printk("SMBFS: Win 95 bug fixes enabled\n");
 	if (mnt->version & SMB_FIX_OLDATTR)
 		printk("SMBFS: Using core getattr (Win 95 speedup)\n");
 	else if (mnt->version & SMB_FIX_DIRATTR)
diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v2.3.6/linux/fs/smbfs/proc.c	Tue Nov  3 21:56:58 1998
+++ linux/fs/smbfs/proc.c	Wed Jun 16 19:26:27 1999
@@ -39,6 +39,9 @@
 #define SMB_DIRINFO_SIZE 43
 #define SMB_STATUS_SIZE  21
 
+/* yes, this deliberately has two parts */
+#define DENTRY_PATH(dentry) (dentry)->d_parent->d_name.name,(dentry)->d_name.name
+
 static int smb_proc_setattr_ext(struct smb_sb_info *, struct inode *,
 				struct smb_fattr *);
 static inline int
@@ -174,24 +177,22 @@
 		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
 
 
-extern struct timezone sys_tz;
-
 static time_t
-utc2local(time_t time)
+utc2local(struct smb_sb_info *server, time_t time)
 {
-	return time - sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 :0);
+	return time - server->opt.serverzone*60;
 }
 
 static time_t
-local2utc(time_t time)
+local2utc(struct smb_sb_info *server, time_t time)
 {
-	return time + sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0);
+	return time + server->opt.serverzone*60;
 }
 
 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 
 static time_t
-date_dos2unix(__u16 date, __u16 time)
+date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time)
 {
 	int month, year;
 	time_t secs;
@@ -202,18 +203,19 @@
 	    ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
 						   month < 2 ? 1 : 0) + 3653);
 	/* days since 1.1.70 plus 80's leap day */
-	return local2utc(secs);
+	return local2utc(server, secs);
 }
 
 
 /* Convert linear UNIX date to a MS-DOS time/date pair. */
 
 static void
-date_unix2dos(int unix_date, __u16 *date, __u16 *time)
+date_unix2dos(struct smb_sb_info *server,
+	      int unix_date, __u16 *date, __u16 *time)
 {
 	int day, year, nl_day, month;
 
-	unix_date = utc2local(unix_date);
+	unix_date = utc2local(server, unix_date);
 	*time = (unix_date % 60) / 2 +
 		(((unix_date / 60) % 60) << 5) +
 		(((unix_date / 3600) % 24) << 11);
@@ -355,6 +357,11 @@
 	int error  = server->err;
 	char *class = "Unknown";
 
+#ifdef SMBFS_DEBUG_VERBOSE
+	printk("smb_errno: errcls %d  code %d  from command 0x%x\n",
+		errcls, error, SMB_CMD(server->packet));
+#endif
+
 	if (errcls == ERRDOS)
 		switch (error)
 		{
@@ -456,7 +463,7 @@
 		class = "ERRCMD";
 
 err_unknown:
-	printk("smb_errno: class %s, code %d from command %x\n",
+	printk("smb_errno: class %s, code %d from command 0x%x\n",
 		class, error, SMB_CMD(server->packet));
 	return EIO;
 }
@@ -646,9 +653,27 @@
 	server->generation += 1;
 	server->state = CONN_VALID;
 	error = 0;
+
+	/* check if we have an old smbmount that uses seconds for the 
+	   serverzone */
+	if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60)
+		server->opt.serverzone /= 60;
+
+	/* now that we have an established connection we can detect the server
+	   type and enable bug workarounds */
+	if (server->opt.protocol == SMB_PROTOCOL_NT1 &&
+	    (server->opt.max_xmit < 0x1000) &&
+	    !(server->opt.capabilities & SMB_CAP_NT_SMBS)) {
+		server->mnt->version |= SMB_FIX_WIN95;
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d\n",
-server->opt.protocol, server->opt.max_xmit, server->conn_pid);
+		printk("smb_newconn: detected WIN95 server\n");
+#endif
+	}
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n",
+       server->opt.protocol, server->opt.max_xmit, server->conn_pid,
+       server->opt.capabilities);
 #endif
 
 out:
@@ -755,7 +780,7 @@
 		{
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_open: %s/%s R/W failed, error=%d, retrying R/O\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, error);
+       DENTRY_PATH(dentry), error);
 #endif
 			mode = read_only;
 			goto retry;
@@ -789,7 +814,7 @@
 	if (!inode)
 	{
 		printk("smb_open: no inode for dentry %s/%s\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+		       DENTRY_PATH(dentry));
 		goto out;
 	}
 
@@ -810,7 +835,7 @@
 		{
 #ifdef SMBFS_PARANOIA
 printk("smb_open: %s/%s open failed, result=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, result);
+       DENTRY_PATH(dentry), result);
 #endif
 			goto out;
 		}
@@ -829,8 +854,7 @@
 	{
 #ifdef SMBFS_PARANOIA
 printk("smb_open: %s/%s access denied, access=%x, wish=%x\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
-inode->u.smbfs_i.access, wish);
+       DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish);
 #endif
 		result = -EACCES;
 	}
@@ -845,7 +869,7 @@
 {
 	smb_setup_header(server, SMBclose, 3, 0);
 	WSET(server->packet, smb_vwv0, fileid);
-	DSET(server->packet, smb_vwv1, utc2local(mtime));
+	DSET(server->packet, smb_vwv1, utc2local(server, mtime));
 	return smb_request_ok(server, SMBclose, 0, 0);
 }
 
@@ -946,7 +970,7 @@
 			{
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_close_dentry: closing %s/%s, count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+       DENTRY_PATH(dentry), dentry->d_count);
 #endif
 				smb_proc_close_inode(server, ino);
 			}
@@ -954,7 +978,7 @@
 		}
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_close_dentry: closed %s/%s, count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+       DENTRY_PATH(dentry), dentry->d_count);
 #endif
 	}
 }
@@ -1014,7 +1038,7 @@
 out:
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_read: file %s/%s, count=%d, result=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, count, result);
+       DENTRY_PATH(dentry), count, result);
 #endif
 	smb_unlock_server(server);
 	return result;
@@ -1029,8 +1053,7 @@
 
 #if SMBFS_DEBUG_VERBOSE
 printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, 
-count, offset, server->packet_size);
+       DENTRY_PATH(dentry), count, offset, server->packet_size);
 #endif
 	smb_lock_server(server);
 	p = smb_setup_header(server, SMBwrite, 5, count + 3);
@@ -1063,7 +1086,7 @@
       retry:
 	p = smb_setup_header(server, SMBcreate, 3, 0);
 	WSET(server->packet, smb_vwv0, attr);
-	DSET(server->packet, smb_vwv1, utc2local(ctime));
+	DSET(server->packet, smb_vwv1, utc2local(server, ctime));
 	*p++ = 4;
 	p = smb_encode_path(server, p, dentry, NULL);
 	smb_setup_bcc(server, p);
@@ -1323,7 +1346,7 @@
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_readdir_short: %s/%s, pos=%d\n",
-dir->d_parent->d_name.name, dir->d_name.name, fpos);
+       DENTRY_PATH(dir), fpos);
 #endif
 
 	smb_lock_server(server);
@@ -1804,15 +1827,15 @@
 	 */
 	date = WVAL(resp_data, 0);
 	time = WVAL(resp_data, 2);
-	fattr->f_ctime = date_dos2unix(date, time);
+	fattr->f_ctime = date_dos2unix(server, date, time);
 
 	date = WVAL(resp_data, 4);
 	time = WVAL(resp_data, 6);
-	fattr->f_atime = date_dos2unix(date, time);
+	fattr->f_atime = date_dos2unix(server, date, time);
 
 	date = WVAL(resp_data, 8);
 	time = WVAL(resp_data, 10);
-	fattr->f_mtime = date_dos2unix(date, time);
+	fattr->f_mtime = date_dos2unix(server, date, time);
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n",
 mask, date, time, fattr->f_mtime);
@@ -1831,7 +1854,7 @@
  */
 static int
 smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
-			struct smb_fattr *fattr)
+		      struct smb_fattr *fattr)
 {
 	int result;
 	char *p;
@@ -1849,13 +1872,13 @@
 		goto out;
 	}
 	fattr->attr    = WVAL(server->packet, smb_vwv0);
-	fattr->f_mtime = local2utc(DVAL(server->packet, smb_vwv1));
+	fattr->f_mtime = local2utc(server, DVAL(server->packet, smb_vwv1));
 	fattr->f_size  = DVAL(server->packet, smb_vwv3);
 	fattr->f_ctime = fattr->f_mtime; 
 	fattr->f_atime = fattr->f_mtime; 
 #ifdef SMBFS_DEBUG_TIMESTAMP
 printk("getattr_core: %s/%s, mtime=%ld\n",
-dir->d_name.name, name->name, fattr->f_mtime);
+       DENTRY_PATH(dir), fattr->f_mtime);
 #endif
 	result = 0;
 
@@ -1926,18 +1949,18 @@
 	}
 	date = WVAL(resp_data, off_date);
 	time = WVAL(resp_data, off_time);
-	attr->f_ctime = date_dos2unix(date, time);
+	attr->f_ctime = date_dos2unix(server, date, time);
 
 	date = WVAL(resp_data, 4 + off_date);
 	time = WVAL(resp_data, 4 + off_time);
-	attr->f_atime = date_dos2unix(date, time);
+	attr->f_atime = date_dos2unix(server, date, time);
 
 	date = WVAL(resp_data, 8 + off_date);
 	time = WVAL(resp_data, 8 + off_time);
-	attr->f_mtime = date_dos2unix(date, time);
+	attr->f_mtime = date_dos2unix(server, date, time);
 #ifdef SMBFS_DEBUG_TIMESTAMP
 printk("getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
-dir->d_name.name, name->name, date, time, attr->f_mtime);
+       DENTRY_PATH(dir), date, time, attr->f_mtime);
 #endif
 	attr->f_size = DVAL(resp_data, 12);
 	attr->attr = WVAL(resp_data, 20);
@@ -1980,6 +2003,7 @@
 	return result;
 }
 
+
 /*
  * Called with the server locked. Because of bugs in the
  * core protocol, we use this only to set attributes. See
@@ -1994,7 +2018,7 @@
  */
 static int
 smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
-			__u16 attr)
+		      __u16 attr)
 {
 	char *p;
 	int result;
@@ -2009,11 +2033,6 @@
 	WSET(server->packet, smb_vwv6, 0);
 	WSET(server->packet, smb_vwv7, 0);
 	*p++ = 4;
-	/*
-	 * Samba uses three leading '\', so we'll do it too.
-	 */
-	*p++ = '\\';
-	*p++ = '\\';
 	p = smb_encode_path(server, p, dentry, NULL);
 	*p++ = 4;
 	*p++ = 0;
@@ -2044,7 +2063,7 @@
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_setattr: setting %s/%s, open=%d\n", 
-dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode));
+       DENTRY_PATH(dir), smb_is_open(dir->d_inode));
 #endif
 	smb_lock_server(server);
 	result = smb_proc_setattr_core(server, dir, fattr->attr);
@@ -2069,10 +2088,10 @@
 	/* We don't change the creation time */
 	WSET(server->packet, smb_vwv1, 0);
 	WSET(server->packet, smb_vwv2, 0);
-	date_unix2dos(fattr->f_atime, &date, &time);
+	date_unix2dos(server, fattr->f_atime, &date, &time);
 	WSET(server->packet, smb_vwv3, date);
 	WSET(server->packet, smb_vwv4, time);
-	date_unix2dos(fattr->f_mtime, &date, &time);
+	date_unix2dos(server, fattr->f_mtime, &date, &time);
 	WSET(server->packet, smb_vwv5, date);
 	WSET(server->packet, smb_vwv6, time);
 #ifdef SMBFS_DEBUG_TIMESTAMP
@@ -2119,15 +2138,15 @@
 
 	WSET(data, 0, 0); /* creation time */
 	WSET(data, 2, 0);
-	date_unix2dos(fattr->f_atime, &date, &time);
+	date_unix2dos(server, fattr->f_atime, &date, &time);
 	WSET(data, 4, date);
 	WSET(data, 6, time);
-	date_unix2dos(fattr->f_mtime, &date, &time);
+	date_unix2dos(server, fattr->f_mtime, &date, &time);
 	WSET(data, 8, date);
 	WSET(data, 10, time);
 #ifdef SMBFS_DEBUG_TIMESTAMP
 printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", 
-dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime);
+       DENTRY_PATH(dir), date, time, fattr->f_mtime);
 #endif
 	DSET(data, 12, 0); /* size */
 	DSET(data, 16, 0); /* blksize */
@@ -2174,10 +2193,12 @@
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_settime: setting %s/%s, open=%d\n", 
-dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
+       DENTRY_PATH(dentry), smb_is_open(inode));
 #endif
 	smb_lock_server(server);
-	if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
+	/* setting the time on a Win95 server fails (tridge) */
+	if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && 
+	    !(server->mnt->version & SMB_FIX_WIN95))
 	{
 		if (smb_is_open(inode) &&
 		    inode->u.smbfs_i.access != SMB_O_RDONLY)
@@ -2194,12 +2215,13 @@
 		{
 			/*
 			 * Set the mtime by opening and closing the file.
+			 * Note that the file is opened read-only, but this
+			 * still allows us to set the date (tridge)
 			 */
 			result = -EACCES;
 			if (!smb_is_open(inode))
-				smb_proc_open(server, dentry, SMB_O_WRONLY);
-			if (smb_is_open(inode) &&
-			    inode->u.smbfs_i.access != SMB_O_RDONLY)
+				smb_proc_open(server, dentry, SMB_O_RDONLY);
+			if (smb_is_open(inode))
 			{
 				inode->i_mtime = fattr->f_mtime;
 				result = smb_proc_close_inode(server, inode);
diff -u --recursive --new-file v2.3.6/linux/fs/super.c linux/fs/super.c
--- v2.3.6/linux/fs/super.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/super.c	Thu Jun 17 23:18:29 1999
@@ -918,13 +918,6 @@
 	int retval;
 	struct vfsmount *vfsmnt;
 	
-	/*
-	 * Invalidate the inodes, as some mount options may be changed.
-	 * N.B. If we are changing media, we should check the return
-	 * from invalidate_inodes ... can't allow _any_ open files.
-	 */
-	invalidate_inodes(sb);
-
 	if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
 		return -EACCES;
 		/*flags |= MS_RDONLY;*/
@@ -941,6 +934,14 @@
 	vfsmnt = lookup_vfsmnt(sb->s_dev);
 	if (vfsmnt)
 		vfsmnt->mnt_flags = sb->s_flags;
+
+	/*
+	 * Invalidate the inodes, as some mount options may be changed.
+	 * N.B. If we are changing media, we should check the return
+	 * from invalidate_inodes ... can't allow _any_ open files.
+	 */
+	invalidate_inodes(sb);
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.6/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v2.3.6/linux/fs/sysv/file.c	Mon Aug 24 13:02:44 1998
+++ linux/fs/sysv/file.c	Sat Jun 19 12:15:14 1999
@@ -33,7 +33,51 @@
 #include <linux/fs.h>
 #include <linux/sysv_fs.h>
 
-static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *);
+static int sysv_writepage (struct file * file, struct page * page)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	unsigned long block;
+	int *p, nr[PAGE_SIZE/512];
+	int i, err, created;
+	struct buffer_head *bh;
+
+	i = PAGE_SIZE >> inode->i_sb->sv_block_size_bits;
+	block = page->offset >> inode->i_sb->sv_block_size_bits;
+	p = nr;
+	bh = page->buffers;
+	do {
+		if (bh && bh->b_blocknr)
+			*p = bh->b_blocknr;
+		else
+			*p = sysv_getblk_block (inode, block, 1, &err, &created);
+		if (!*p)
+			return -EIO;
+		i--;
+		block++;
+		p++;
+		if (bh)
+			bh = bh->b_this_page;
+	} while (i > 0);
+
+	/* IO start */
+	brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->sv_block_size, 1);
+	return 0;
+}
+
+static long sysv_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
+{
+	return block_write_one_page(file, page, offset, bytes, buf, sysv_getblk_block);
+}
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	return generic_file_write(file, buf, count, ppos, sysv_write_one_page);
+}
 
 /*
  * We have mostly NULLs here: the current defaults are OK for
@@ -41,7 +85,7 @@
  */
 static struct file_operations sysv_file_operations = {
 	NULL,			/* lseek - default */
-	sysv_file_read,		/* read */
+	generic_file_read,	/* read */
 	sysv_file_write,	/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* poll - default */
@@ -50,7 +94,10 @@
 	NULL,			/* no special open is needed */
 	NULL,			/* flush */
 	NULL,			/* release */
-	sysv_sync_file		/* fsync */
+	sysv_sync_file,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
 };
 
 struct inode_operations sysv_file_inode_operations = {
@@ -67,208 +114,11 @@
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	generic_readpage,	/* readpage */
-	NULL,			/* writepage */
+	sysv_writepage,		/* writepage */
 	sysv_bmap,		/* bmap */
 	sysv_truncate,		/* truncate */
-	NULL			/* permission */
+	NULL,   		/* permission */
+	NULL,			/* smap */
+	NULL,			/* revalidate */
+	block_flushpage,	/* flushpage */
 };
-
-ssize_t sysv_file_read(struct file * filp, char * buf, 
-		       size_t count, loff_t *ppos)
-{
-	struct inode * inode = filp->f_dentry->d_inode;
-	struct super_block * sb = inode->i_sb;
-	ssize_t read,left,chars;
-	size_t block;
-	ssize_t blocks, offset;
-	int bhrequest, uptodate;
-	struct buffer_head ** bhb, ** bhe;
-	struct buffer_head * bhreq[NBUF];
-	struct buffer_head * buflist[NBUF];
-	size_t size;
-
-	if (!inode) {
-		printk("sysv_file_read: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!S_ISREG(inode->i_mode)) {
-		printk("sysv_file_read: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	offset = *ppos;
-	size = inode->i_size;
-	if (offset > size)
-		left = 0;
-	else
-		left = size - offset;
-	if (left > count)
-		left = count;
-	if (left <= 0)
-		return 0;
-	read = 0;
-	block = offset >> sb->sv_block_size_bits;
-	offset &= sb->sv_block_size_1;
-	size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits;
-	blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
-	bhb = bhe = buflist;
-	if (filp->f_reada) {
-		blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9);
-		if (block + blocks > size)
-			blocks = size - block;
-	}
-
-	/* We do this in a two stage process.  We first try to request
-	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try to wrap up as many as are actually
-	   done.  This routine is rather generic, in that it can be used
-	   in a filesystem by substituting the appropriate function in
-	   for getblk.
-
-	   This routine is optimized to make maximum use of the various
-	   buffers and caches.
-	 */
-
-	do {
-		bhrequest = 0;
-		uptodate = 1;
-		while (blocks) {
-			--blocks;
-			*bhb = sysv_getblk(inode, block++, 0);
-			if (*bhb && !buffer_uptodate(*bhb)) {
-				uptodate = 0;
-				bhreq[bhrequest++] = *bhb;
-			}
-
-			if (++bhb == &buflist[NBUF])
-				bhb = buflist;
-
-			/* If the block we have on hand is uptodate, go ahead
-			   and complete processing. */
-			if (uptodate)
-				break;
-			if (bhb == bhe)
-				break;
-		}
-
-		/* Now request them all */
-		if (bhrequest)
-			ll_rw_block(READ, bhrequest, bhreq);
-
-		do { /* Finish off all I/O that has actually completed */
-			if (*bhe) {
-				wait_on_buffer(*bhe);
-				if (!buffer_uptodate(*bhe)) {	/* read error? */
-					brelse(*bhe);
-					if (++bhe == &buflist[NBUF])
-						bhe = buflist;
-					left = 0;
-					break;
-				}
-			}
-			if (left < sb->sv_block_size - offset)
-				chars = left;
-			else
-				chars = sb->sv_block_size - offset;
-			*ppos += chars;
-			left -= chars;
-			read += chars;
-			if (*bhe) {
-				copy_to_user(buf,offset+(*bhe)->b_data,chars);
-				brelse(*bhe);
-				buf += chars;
-			} else {
-				while (chars-- > 0)
-					put_user(0,buf++);
-			}
-			offset = 0;
-			if (++bhe == &buflist[NBUF])
-				bhe = buflist;
-		} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
-	} while (left > 0);
-
-/* Release the read-ahead blocks */
-	while (bhe != bhb) {
-		brelse(*bhe);
-		if (++bhe == &buflist[NBUF])
-			bhe = buflist;
-	};
-	if (!read)
-		return -EIO;
-	filp->f_reada = 1;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		mark_inode_dirty(inode);
-	}
-	return read;
-}
-
-static ssize_t sysv_file_write(struct file * filp, const char * buf,
-			       size_t count, loff_t *ppos)
-{
-	struct inode * inode = filp->f_dentry->d_inode;
-	struct super_block * sb = inode->i_sb;
-	off_t pos;
-	ssize_t written, c;
-	struct buffer_head * bh;
-	char * p;
-
-	if (!inode) {
-		printk("sysv_file_write: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!S_ISREG(inode->i_mode)) {
-		printk("sysv_file_write: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-/*
- * OK, append may not work when many processes are writing at the same time
- * but so what. That way leads to madness anyway.
- * But we need to protect against simultaneous truncate as we may end up
- * writing our data into blocks that have meanwhile been incorporated into
- * the freelist, thereby trashing the freelist.
- */
-	if (filp->f_flags & O_APPEND)
-		pos = inode->i_size;
-	else
-		pos = *ppos;
-	written = 0;
-	while (written<count) {
-		bh = sysv_getblk (inode, pos >> sb->sv_block_size_bits, 1);
-		if (!bh) {
-			if (!written)
-				written = -ENOSPC;
-			break;
-		}
-		c = sb->sv_block_size - (pos & sb->sv_block_size_1);
-		if (c > count-written)
-			c = count-written;
-		if (c != sb->sv_block_size && !buffer_uptodate(bh)) {
-			ll_rw_block(READ, 1, &bh);
-			wait_on_buffer(bh);
-			if (!buffer_uptodate(bh)) {
-				brelse(bh);
-				if (!written)
-					written = -EIO;
-				break;
-			}
-		}
-		/* now either c==sb->sv_block_size or buffer_uptodate(bh) */
-		p = (pos & sb->sv_block_size_1) + bh->b_data;
-		copy_from_user(p, buf, c);
-		update_vm_cache(inode, pos, p, c);
-		pos += c;
-		if (pos > inode->i_size) {
-			inode->i_size = pos;
-			mark_inode_dirty(inode);
-		}
-		written += c;
-		buf += c;
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 0);
-		brelse(bh);
-	}
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	*ppos = pos;
-	mark_inode_dirty(inode);
-	return written;
-}
diff -u --recursive --new-file v2.3.6/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.3.6/linux/fs/sysv/inode.c	Tue Jun  8 10:47:58 1999
+++ linux/fs/sysv/inode.c	Wed Jun 16 19:26:27 1999
@@ -657,7 +657,8 @@
 
 /* Access selected blocks of regular files (or directories) */
 
-static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
+static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create,
+					 int metadata, int *phys_block, int *created)
 {
 	struct super_block *sb;
 	u32 tmp;
@@ -669,31 +670,48 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = sv_getblk(sb, inode->i_dev, tmp);
-		if (tmp == *p)
-			return result;
-		brelse(result);
-		goto repeat;
+		if (metadata) {
+			result = sv_getblk(sb, inode->i_dev, tmp);
+			if (tmp == *p)
+				return result;
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
+		}
 	}
 	if (!create)
 		return NULL;
 	tmp = sysv_new_block(sb);
 	if (!tmp)
 		return NULL;
-	result = sv_getblk(sb, inode->i_dev, tmp);
-	if (*p) {
-		sysv_free_block(sb,tmp);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = sv_getblk(sb, inode->i_dev, tmp);
+		if (*p) {
+			sysv_free_block(sb, tmp);
+			brelse(result);
+			goto repeat;
+		}
+	} else {
+		if (*p) {
+			sysv_free_block(sb, tmp);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
 	*p = tmp;
+
 	inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
 	return result;
 }
 
 static struct buffer_head * block_getblk(struct inode * inode,
-	struct buffer_head * bh, int nr, int create)
+	struct buffer_head * bh, int nr, int create,
+	int metadata, int *phys_block, int *created)
 {
 	struct super_block *sb;
 	u32 tmp, block;
@@ -717,13 +735,19 @@
 	if (sb->sv_convert)
 		block = from_coh_ulong(block);
 	if (tmp) {
-		result = sv_getblk(sb, bh->b_dev, block);
-		if (tmp == *p) {
+		if (metadata) {
+			result = sv_getblk(sb, bh->b_dev, block);
+			if (tmp == *p) {
+				brelse(bh);
+				return result;
+			}
+			brelse(result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse(bh);
-			return result;
+			return NULL;
 		}
-		brelse(result);
-		goto repeat;
 	}
 	if (!create) {
 		brelse(bh);
@@ -734,11 +758,17 @@
 		brelse(bh);
 		return NULL;
 	}
-	result = sv_getblk(sb, bh->b_dev, block);
-	if (*p) {
-		sysv_free_block(sb,block);
-		brelse(result);
-		goto repeat;
+	if (metadata) {
+		result = sv_getblk(sb, bh->b_dev, block);
+		if (*p) {
+			sysv_free_block(sb,block);
+			brelse(result);
+			goto repeat;
+		}
+	} else {
+		*phys_block = tmp;
+		result = NULL;
+		*created = 1;
 	}
 	*p = (sb->sv_convert ? to_coh_ulong(block) : block);
 	mark_buffer_dirty(bh, 1);
@@ -746,37 +776,74 @@
 	return result;
 }
 
-struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block, int create)
+int sysv_getblk_block(struct inode *inode, long block, int create,
+		      int *err, int *created)
 {
-	struct super_block * sb = inode->i_sb;
-	struct buffer_head * bh;
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh, *tmp;
+	int phys_block;
 
-	if (block < 10)
-		return inode_getblk(inode,block,create);
+	*err = -EIO;
+	if (block < 0) {
+		printk("sysv_getblk: block<0");
+		return 0;
+	}
+	if (block > sb->sv_ind_per_block_3) {
+		printk("sysv_getblk: block>big");
+		return 0;
+	}
+	if (block < 10) {
+		tmp = inode_getblk(inode, block, create,
+				   0, &phys_block, created);
+		goto out;
+	}
 	block -= 10;
 	if (block < sb->sv_ind_per_block) {
-		bh = inode_getblk(inode,10,create);
-		return block_getblk(inode, bh, block, create);
+		bh = inode_getblk(inode, 10, create, 1, NULL, NULL);
+		tmp = block_getblk(inode, bh, block, create,
+				   0, &phys_block, created);
+		goto out;
 	}
 	block -= sb->sv_ind_per_block;
 	if (block < sb->sv_ind_per_block_2) {
-		bh = inode_getblk(inode,11,create);
-		bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create);
-		return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create);
+		bh = inode_getblk(inode, 11, create, 1, NULL, NULL);
+		bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create,
+				  1, NULL, NULL);
+		tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create,
+				   0, &phys_block, created);
+		goto out;
 	}
 	block -= sb->sv_ind_per_block_2;
-	if (block < sb->sv_ind_per_block_3) {
-		bh = inode_getblk(inode,12,create);
-		bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create);
-		bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create);
-		return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create);
-	}
-	if ((int)block<0) {
-		printk("sysv_getblk: block<0");
-		return NULL;
+	bh = inode_getblk(inode, 12, create, 1, NULL, NULL);
+	bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create,
+			  1, NULL, NULL);
+	bh = block_getblk(inode, bh,
+			  (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,
+			  create, 1, NULL, NULL);
+	tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create,
+			   0, &phys_block, created);
+
+out:
+	*err = 0;
+	return phys_block;
+}
+
+struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create)
+{
+	struct buffer_head *tmp = NULL;
+	int phys_block;
+	int err, created;
+
+	phys_block = sysv_getblk_block(inode, block, create, &err, &created);
+	if (phys_block) {
+		tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE);
+		if (created) {
+			memset(tmp->b_data, 0, BLOCK_SIZE);
+			mark_buffer_uptodate(tmp, 1);
+			mark_buffer_dirty(tmp, 1);
+		}
 	}
-	printk("sysv_getblk: block>big");
-	return NULL;
+	return tmp;
 }
 
 struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create)
diff -u --recursive --new-file v2.3.6/linux/fs/sysv/truncate.c linux/fs/sysv/truncate.c
--- v2.3.6/linux/fs/sysv/truncate.c	Sun Sep 13 10:27:07 1998
+++ linux/fs/sysv/truncate.c	Wed Jun 16 19:26:27 1999
@@ -35,6 +35,9 @@
  * general case (size = XXX). I hope.
  */
 
+#define DATA_BUFFER_USED(bh) \
+	((bh->b_count > 1) || buffer_locked(bh))
+
 /* We throw away any data beyond inode->i_size. */
 
 static int trunc_direct(struct inode * inode)
@@ -58,7 +61,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || (block != *p)) {
+		if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) {
 			retry = 1;
 			brelse(bh);
 			continue;
@@ -115,7 +118,7 @@
 			brelse(bh);
 			goto repeat;
 		}
-		if ((bh && bh->b_count != 1) || (tmp != *ind)) {
+		if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) {
 			retry = 1;
 			brelse(bh);
 			continue;
@@ -128,7 +131,7 @@
 	for (i = 0; i < sb->sv_ind_per_block; i++)
 		if (((sysv_zone_t *) indbh->b_data)[i])
 			goto done;
-	if ((indbh->b_count != 1) || (indtmp != *p)) {
+	if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
 		brelse(indbh);
 		return 1;
 	}
@@ -185,7 +188,7 @@
 	for (i = 0; i < sb->sv_ind_per_block; i++)
 		if (((sysv_zone_t *) indbh->b_data)[i])
 			goto done;
-	if ((indbh->b_count != 1) || (indtmp != *p)) {
+	if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
 		brelse(indbh);
 		return 1;
 	}
@@ -242,7 +245,7 @@
 	for (i = 0; i < sb->sv_ind_per_block; i++)
 		if (((sysv_zone_t *) indbh->b_data)[i])
 			goto done;
-	if ((indbh->b_count != 1) || (indtmp != *p)) {
+	if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
 		brelse(indbh);
 		return 1;
 	}
diff -u --recursive --new-file v2.3.6/linux/fs/ufs/file.c linux/fs/ufs/file.c
--- v2.3.6/linux/fs/ufs/file.c	Thu Jan 14 10:31:41 1999
+++ linux/fs/ufs/file.c	Sat Jun 19 12:15:14 1999
@@ -41,52 +41,6 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-static long long ufs_file_lseek(struct file *, long long, int);
-static ssize_t ufs_file_write (struct file *, const char *, size_t, loff_t *);
-static int ufs_release_file (struct inode *, struct file *);
-
-/*
- * We have mostly NULL's here: the current defaults are ok for
- * the ufs filesystem.
- */
-static struct file_operations ufs_file_operations = {
-	ufs_file_lseek,	/* lseek */
-	generic_file_read,	/* read */
-	ufs_file_write, 	/* write */
-	NULL,			/* readdir - bad */
-	NULL,			/* poll - default */
-	NULL, 			/* ioctl */
-	generic_file_mmap,	/* mmap */
-	NULL,			/* no special open is needed */
-	NULL,			/* flush */
-	ufs_release_file,	/* release */
-	NULL, 			/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* check_media_change */
-	NULL			/* revalidate */
-};
-
-struct inode_operations ufs_file_inode_operations = {
-	&ufs_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 */
-	ufs_bmap,		/* bmap */
-	ufs_truncate,		/* truncate */
-	NULL, 			/* permission */
-	NULL			/* smap */
-};
-
 /*
  * Make sure the offset never goes beyond the 32-bit mark..
  */
@@ -133,139 +87,49 @@
 	}
 }
 
-static ssize_t ufs_file_write (
-	struct file * filp,
-	const char * buf,
-	size_t count,
-	loff_t *ppos )
-{
-	struct inode * inode = filp->f_dentry->d_inode;
-	__u32 pos;
-	long block;
-	int offset;
-	int written, c;
-	struct buffer_head * bh, *bufferlist[NBUF];
-	struct super_block * sb;
-	int err;
-	int i,buffercount,write_error;
-
-	/* POSIX: mtime/ctime may not change for 0 count */
-	if (!count)
-		return 0;
-	write_error = buffercount = 0;
-	if (!inode)
-		return -EINVAL;
-	sb = inode->i_sb;
-	if (sb->s_flags & MS_RDONLY)
-		/*
-		 * This fs has been automatically remounted ro because of errors
-		 */
-		return -ENOSPC;
-
-	if (!S_ISREG(inode->i_mode)) {
-		ufs_warning (sb, "ufs_file_write", "mode = %07o",
-			      inode->i_mode);
-		return -EINVAL;
-	}
-	remove_suid(inode);
-
-	if (filp->f_flags & O_APPEND)
-		pos = inode->i_size;
-	else {
-		pos = *ppos;
-		if (pos != *ppos)
-			return -EINVAL;
-	}
-
-	/* Check for overflow.. */
-	if (pos > (__u32) (pos + count)) {
-		count = ~pos; /* == 0xFFFFFFFF - pos */
-		if (!count)
-			return -EFBIG;
-	}
-
-	/*
-	 * If a file has been opened in synchronous mode, we have to ensure
-	 * that meta-data will also be written synchronously.  Thus, we
-	 * set the i_osync field.  This field is tested by the allocation
-	 * routines.
-	 */
-	if (filp->f_flags & O_SYNC)
-		inode->u.ufs_i.i_osync++;
-	block = pos >> sb->s_blocksize_bits;
-	offset = pos & (sb->s_blocksize - 1);
-	c = sb->s_blocksize - offset;
-	written = 0;
+static int ufs_writepage (struct file *file, struct page *page)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	unsigned long block;
+	int *p, nr[PAGE_SIZE/512];
+	int i, err, created;
+	struct buffer_head *bh;
+
+	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+	block = page->offset >> inode->i_sb->s_blocksize_bits;
+	p = nr;
+	bh = page->buffers;
 	do {
-		bh = ufs_getfrag (inode, block, 1, &err);
-		if (!bh) {
-			if (!written)
-				written = err;
-			break;
-		}
-		if (c > count)
-			c = count;
-		if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
-			ll_rw_block (READ, 1, &bh);
-			wait_on_buffer (bh);
-			if (!buffer_uptodate(bh)) {
-				brelse (bh);
-				if (!written)
-					written = -EIO;
-				break;
-			}
-		}
-		c -= copy_from_user (bh->b_data + offset, buf, c);
-		if (!c) {
-			brelse(bh);
-			if (!written)
-				written = -EFAULT;
-			break;
-		}
-		update_vm_cache(inode, pos, bh->b_data + offset, c);
-		pos += c;
-		written += c;
-		buf += c;
-		count -= c;
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 0);
-		if (filp->f_flags & O_SYNC)
-			bufferlist[buffercount++] = bh;
+		if (bh && bh->b_blocknr)
+			*p = bh->b_blocknr;
 		else
-			brelse(bh);
-		if (buffercount == NBUF){
-			ll_rw_block(WRITE, buffercount, bufferlist);
-			for(i=0; i<buffercount; i++){
-				wait_on_buffer(bufferlist[i]);
-				if (!buffer_uptodate(bufferlist[i]))
-					write_error=1;
-				brelse(bufferlist[i]);
-			}
-			buffercount=0;
-		}
-		if (write_error)
-			break;
+			*p = ufs_getfrag_block(inode, block, 1, &err, &created);
+		if (!*p)
+			return -EIO;
+		i--;
 		block++;
-		offset = 0;
-		c = sb->s_blocksize;
-	} while (count);
-	if (buffercount){
-		ll_rw_block(WRITE, buffercount, bufferlist);
-		for (i=0; i<buffercount; i++){
-			wait_on_buffer(bufferlist[i]);
-			if (!buffer_uptodate(bufferlist[i]))
-				write_error=1;
-			brelse(bufferlist[i]);
-		}
-	}		
-	if (pos > inode->i_size)
-		inode->i_size = pos;
-	if (filp->f_flags & O_SYNC)
-		inode->u.ufs_i.i_osync--;
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	*ppos = pos;
-	mark_inode_dirty(inode);
-	return written;
+		p++;
+		if (bh)
+			bh = bh->b_this_page;
+	} while (i > 0);
+
+	brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
+	return 0;
+}
+
+static long ufs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf)
+{
+	return block_write_one_page(file, page, offset, bytes, buf, ufs_getfrag_block);
+}
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	return generic_file_write(file, buf, count, ppos, ufs_write_one_page);
 }
 
 /*
@@ -277,3 +141,47 @@
 {
 	return 0;
 }
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ufs filesystem.
+ */
+static struct file_operations ufs_file_operations = {
+	ufs_file_lseek,	/* lseek */
+	generic_file_read,	/* read */
+	ufs_file_write, 	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* poll - default */
+	NULL, 			/* ioctl */
+	generic_file_mmap,	/* mmap */
+	NULL,			/* no special open is needed */
+	NULL,			/* flush */
+	ufs_release_file,	/* release */
+	NULL, 			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+struct inode_operations ufs_file_inode_operations = {
+	&ufs_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 */
+	ufs_writepage,		/* writepage */
+	ufs_bmap,		/* bmap */
+	ufs_truncate,		/* truncate */
+	NULL, 			/* permission */
+	NULL,			/* smap */
+	NULL,			/* revalidate */
+	block_flushpage,	/* flushpage */
+};
diff -u --recursive --new-file v2.3.6/linux/fs/ufs/inode.c linux/fs/ufs/inode.c
--- v2.3.6/linux/fs/ufs/inode.c	Tue May 11 23:01:41 1999
+++ linux/fs/ufs/inode.c	Wed Jun 16 19:26:27 1999
@@ -175,7 +175,7 @@
 
 static struct buffer_head * ufs_inode_getfrag (struct inode * inode, 
 	unsigned fragment, unsigned new_fragment, int create, 
-	unsigned required, int * err )
+	unsigned required, int *err, int metadata, int *phys_block, int *created)
 {
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
@@ -201,13 +201,19 @@
 	tmp = SWAB32(*p);
 	lastfrag = inode->u.ufs_i.i_lastfrag;
 	if (tmp && fragment < lastfrag) {
-		result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize);
-		if (tmp == SWAB32(*p)) {
-			UFSD(("EXIT, result %u\n", tmp + blockoff))
-			return result;
+		if (metadata) {
+			result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff,
+					 sb->s_blocksize);
+			if (tmp == SWAB32(*p)) {
+				UFSD(("EXIT, result %u\n", tmp + blockoff))
+					return result;
+			}
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
 		}
-		brelse (result);
-		goto repeat;
 	}
 	*err = -EFBIG;
 	if (!create)
@@ -269,7 +275,20 @@
 		else
 			return NULL;
 	}
-	result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize);
+
+	/* The nullification of framgents done in ufs/balloc.c is
+	 * something I don't have the stomache to move into here right
+	 * now. -DaveM
+	 */
+	if (metadata) {
+		result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize);
+	} else {
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
+	}
+
 	inode->i_ctime = CURRENT_TIME;
 	if (IS_SYNC(inode))
 		ufs_sync_inode (inode);
@@ -280,7 +299,7 @@
 
 static struct buffer_head * ufs_block_getfrag (struct inode * inode,
 	struct buffer_head * bh, unsigned fragment, unsigned new_fragment, 
-	int create, unsigned blocksize, int * err)
+	int create, unsigned blocksize, int * err, int metadata, int *phys_block, int *created)
 {
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
@@ -312,19 +331,36 @@
 repeat:
 	tmp = SWAB32(*p);
 	if (tmp) {
-		result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize);
-		if (tmp == SWAB32(*p)) {
+		if (metadata) {
+			result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff,
+					 sb->s_blocksize);
+			if (tmp == SWAB32(*p)) {
+				brelse (bh);
+				UFSD(("EXIT, result %u\n", tmp + blockoff))
+				return result;
+			}
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse (bh);
-			UFSD(("EXIT, result %u\n", tmp + blockoff))
-			return result;
+			return NULL;
 		}
-		brelse (result);
-		goto repeat;
 	}
-	if (!create || new_fragment >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> sb->s_blocksize)) {
+	*err = -EFBIG;
+	if (!create) {
 		brelse (bh);
-		*err = -EFBIG;
 		return NULL;
+	} else {
+		unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+		if (limit < RLIM_INFINITY) {
+			limit >>= sb->s_blocksize_bits;
+			if (new_fragment >= limit) {
+				brelse (bh);
+				send_sig(SIGXFSZ, current, 0);
+				return NULL;
+			}
+		}
 	}
 	if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb))
 		goal = tmp + uspi->s_fpb;
@@ -334,12 +370,25 @@
 	if (!tmp) {
 		if (SWAB32(*p)) {
 			goto repeat;
-		}
-		else {
+		} else {
+			brelse (bh);
 			return NULL;
 		}
 	}		
-	result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize);
+
+	/* The nullification of framgents done in ufs/balloc.c is
+	 * something I don't have the stomache to move into here right
+	 * now. -DaveM
+	 */
+	if (metadata) {
+		result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize);
+	} else {
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
+	}
+
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(inode)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -352,14 +401,15 @@
 	return result;
 }
 
-struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment,
-	int create, int * err)
+int ufs_getfrag_block (struct inode * inode, long fragment,
+		       int create, int * err, int *created)
 {
 	struct super_block * sb;
 	struct ufs_sb_private_info * uspi;
-	struct buffer_head * bh;
+	struct buffer_head * bh, * tmp;
 	unsigned f;
 	unsigned swab;
+	int phys_block;
 	
 	sb = inode->i_sb;
 	uspi = sb->u.ufs_sb.s_uspi;
@@ -367,19 +417,27 @@
 	*err = -EIO;
 
 	UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment))
+	if (fragment < 0) {
+		ufs_warning (sb, "ufs_getblk", "block < 0");
+		return 0;
+	}
 	if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) {
 		ufs_warning (sb, "ufs_getblk", "block > big");
-		return NULL;
+		return 0;
 	}
 
 	*err = -ENOSPC;
 	f = fragment;
+	*created = 0;
 	  
 	/*
 	 * Direct fragment
 	 */
-	if (fragment < UFS_NDIR_FRAGMENT)
-		return ufs_inode_getfrag (inode, fragment, fragment, create, 1, err);
+	if (fragment < UFS_NDIR_FRAGMENT) {
+		tmp = ufs_inode_getfrag (inode, fragment, fragment, create, 1,
+					 err, 0, &phys_block, created);
+		goto out;
+	}
 	/*
 	 * Indirect fragment
 	 */
@@ -387,10 +445,12 @@
 	if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) {
 		bh = ufs_inode_getfrag (inode, 
 			UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift),
-			f, create, uspi->s_fpb, err);
-		return ufs_block_getfrag (inode, bh, 
-			fragment & uspi->s_apbmask,
-			f, create, sb->s_blocksize, err);
+			f, create, uspi->s_fpb, err, 1, NULL, NULL);
+		tmp = ufs_block_getfrag (inode, bh, 
+					 fragment & uspi->s_apbmask,
+					 f, create, sb->s_blocksize,
+					 err, 0, &phys_block, created);
+		goto out;
 	}
 	/*
 	 * Dindirect fragment
@@ -398,14 +458,18 @@
 	fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift);
 	if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) {
 		bh = ufs_inode_getfrag (inode,
-			UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), 
-			f, create, uspi->s_fpb, err);
+			UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift),
+			f, create, uspi->s_fpb, err,
+			1, NULL, NULL);
 		bh = ufs_block_getfrag (inode, bh,
 			(fragment >> uspi->s_apbshift) & uspi->s_apbmask, 
-			f, create, sb->s_blocksize, err);
-		return ufs_block_getfrag (inode, bh, 
+			f, create, sb->s_blocksize, err,
+			1, NULL, NULL);
+		tmp = ufs_block_getfrag (inode, bh, 
 			fragment & uspi->s_apbmask,
-			f, create, sb->s_blocksize, err);
+			f, create, sb->s_blocksize, err,
+			0, &phys_block, created);
+		goto out;
 	}
 	/*
 	 * Tindirect fragment
@@ -413,19 +477,42 @@
 	fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift);
 	bh = ufs_inode_getfrag (inode,
 		UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), 
-		f, create, uspi->s_fpb, err);
+		f, create, uspi->s_fpb, err, 1, NULL, NULL);
 	bh = ufs_block_getfrag (inode, bh,
 		(fragment >> uspi->s_2apbshift) & uspi->s_apbmask,
-		f, create, sb->s_blocksize, err);
+		f, create, sb->s_blocksize, err, 1, NULL, NULL);
 	bh = ufs_block_getfrag (inode, bh,
 		(fragment >> uspi->s_apbshift) & uspi->s_apbmask, 
-		f, create, sb->s_blocksize, err);
-	return ufs_block_getfrag (inode, bh,
+		f, create, sb->s_blocksize, err, 1, NULL, NULL);
+	tmp = ufs_block_getfrag (inode, bh,
 		fragment & uspi->s_apbmask, 
-		f, create, sb->s_blocksize, err);
+		f, create, sb->s_blocksize, err, 0, &phys_block, created);
+
+out:
+	if (!phys_block)
+		return 0;
+	if (*err)
+		return 0;
+	return phys_block;
 }
 
+struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment,
+				int create, int *err)
+{
+	struct buffer_head *tmp = NULL;
+	int phys_block, created;
 
+	phys_block = ufs_getfrag_block(inode, fragment, create, err, &created);
+	if (phys_block) {
+		tmp = getblk(inode->i_dev, phys_block, inode->i_sb->s_blocksize);
+		if (created) {
+			memset(tmp->b_data, 0, inode->i_sb->s_blocksize);
+			mark_buffer_uptodate(tmp, 1);
+			mark_buffer_dirty(tmp, 1);
+		}
+	}
+	return tmp;
+}
 
 struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment,
 	int create, int * err)
diff -u --recursive --new-file v2.3.6/linux/fs/ufs/truncate.c linux/fs/ufs/truncate.c
--- v2.3.6/linux/fs/ufs/truncate.c	Tue Sep  1 10:50:11 1998
+++ linux/fs/ufs/truncate.c	Wed Jun 16 19:26:27 1999
@@ -62,6 +62,9 @@
 #define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize)
 #define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize)
 
+#define DATA_BUFFER_USED(bh) \
+	((bh->b_count > 1) || buffer_locked(bh))
+
 static int ufs_trunc_direct (struct inode * inode)
 {
 	struct super_block * sb;
@@ -114,7 +117,7 @@
 	frag2 = ufs_fragnum (frag2);
 	for (j = frag1; j < frag2; j++) {
 		bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
-		if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) {
 			retry = 1;
 			brelse (bh);
 			goto next1;
@@ -137,7 +140,7 @@
 			continue;
 		for (j = 0; j < uspi->s_fpb; j++) {
 			bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
-			if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) {
+			if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) {
 				retry = 1;
 				brelse (bh);
 				goto next2;
@@ -176,7 +179,7 @@
 	frag4 = ufs_fragnum (frag4);
 	for (j = 0; j < frag4; j++) {
 		bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
-		if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) {
+		if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) {
 			retry = 1;
 			brelse (bh);
 			goto next1;
@@ -237,7 +240,7 @@
 			continue;
 		for (j = 0; j < uspi->s_fpb; j++) {
 			bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
-			if ((bh && bh->b_count != 1) || tmp != SWAB32(*ind)) {
+			if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*ind)) {
 				retry = 1;
 				brelse (bh);
 				goto next;
diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.3.6/linux/fs/umsdos/dir.c	Thu May 13 23:18:21 1999
+++ linux/fs/umsdos/dir.c	Sat Jun 19 11:45:28 1999
@@ -838,6 +838,5 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL,			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.3.6/linux/fs/umsdos/rdir.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/umsdos/rdir.c	Sat Jun 19 11:45:29 1999
@@ -253,6 +253,5 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL,			/* revalidate */
 };
diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.3.6/linux/fs/umsdos/symlink.c	Sat Sep 19 13:46:28 1998
+++ linux/fs/umsdos/symlink.c	Sat Jun 19 11:45:29 1999
@@ -141,7 +141,6 @@
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
-	NULL,			/* updatepage */
 	NULL			/* revalidate */
 };
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-arc/ide.h linux/include/asm-arm/arch-arc/ide.h
--- v2.3.6/linux/include/asm-arm/arch-arc/ide.h	Thu May 13 11:00:08 1999
+++ linux/include/asm-arm/arch-arc/ide.h	Thu Jun 17 01:11:35 1999
@@ -19,7 +19,8 @@
  * Set up a hw structure for a specified data port, control port and IRQ.
  * This should follow whatever the default interface uses.
  */
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
 {
 	ide_ioreg_t reg = (ide_ioreg_t) data_port;
 	int i;
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/ide.h linux/include/asm-arm/arch-ebsa285/ide.h
--- v2.3.6/linux/include/asm-arm/arch-ebsa285/ide.h	Thu May 13 11:00:08 1999
+++ linux/include/asm-arm/arch-ebsa285/ide.h	Thu Jun 17 01:11:35 1999
@@ -12,7 +12,8 @@
  * Set up a hw structure for a specified data port, control port and IRQ.
  * This should follow whatever the default interface uses.
  */
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq)
 {
 	ide_ioreg_t reg = (ide_ioreg_t) data_port;
 	int i;
@@ -22,7 +23,7 @@
 		reg += 1;
 	}
 	hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
-	hw->irq = *irq;
+	hw->irq = irq;
 }
 
 /*
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/irq.h linux/include/asm-arm/arch-ebsa285/irq.h
--- v2.3.6/linux/include/asm-arm/arch-ebsa285/irq.h	Wed May 12 13:16:27 1999
+++ linux/include/asm-arm/arch-ebsa285/irq.h	Thu Jun 17 01:11:35 1999
@@ -10,7 +10,6 @@
  *  26-Jan-1999	PJB	Don't use IACK on CATS
  *  16-Mar-1999	RMK	Added autodetect of ISA PICs
  */
-#include <linux/config.h>
 #include <asm/hardware.h>
 #include <asm/dec21285.h>
 #include <asm/irq.h>
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/memory.h linux/include/asm-arm/arch-ebsa285/memory.h
--- v2.3.6/linux/include/asm-arm/arch-ebsa285/memory.h	Wed May 12 13:16:27 1999
+++ linux/include/asm-arm/arch-ebsa285/memory.h	Thu Jun 17 01:11:35 1999
@@ -15,8 +15,6 @@
 #ifndef __ASM_ARCH_MMU_H
 #define __ASM_ARCH_MMU_H
 
-#include <linux/config.h>
-
 #if defined(CONFIG_HOST_FOOTBRIDGE)
 
 /*
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/system.h linux/include/asm-arm/arch-ebsa285/system.h
--- v2.3.6/linux/include/asm-arm/arch-ebsa285/system.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/system.h	Thu Jun 17 01:11:35 1999
@@ -20,16 +20,7 @@
 		 mcr	p15, 0, ip, c7, c7	@ flush caches
 		 mov	pc, lr" : : : "cc");
 	} 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 +39,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 -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/time.h linux/include/asm-arm/arch-ebsa285/time.h
--- v2.3.6/linux/include/asm-arm/arch-ebsa285/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/time.h	Thu Jun 17 01:11:35 1999
@@ -333,7 +333,7 @@
 		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 -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-rpc/ide.h linux/include/asm-arm/arch-rpc/ide.h
--- v2.3.6/linux/include/asm-arm/arch-rpc/ide.h	Thu May 13 11:00:08 1999
+++ linux/include/asm-arm/arch-rpc/ide.h	Thu Jun 17 01:11:35 1999
@@ -12,30 +12,31 @@
  * Set up a hw structure for a specified data port, control port and IRQ.
  * This should follow whatever the default interface uses.
  */
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq)
 {
 	ide_ioreg_t reg = (ide_ioreg_t) data_port;
 	int i;
 
+	memset(hw, 0, sizeof(*hw));
+
 	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
 		hw->io_ports[i] = reg;
 		reg += 1;
 	}
 	hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
-	hw->irq = *irq;
+	hw->irq = irq;
 }
 
 /*
  * This registers the standard ports for this architecture with the IDE
  * driver.
  */
-static __inline__ void ide_init_default_hwifs(void)
+static __inline__ void
+ide_init_default_hwifs(void)
 {
 	hw_regs_t hw;
 
-        memset(hw, 0, sizeof(*hw));
-
-	ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
-	hw.irq = IRQ_HARDDISK;
+	ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK);
 	ide_register_hw(&hw, NULL);
 }
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/current.h linux/include/asm-arm/current.h
--- v2.3.6/linux/include/asm-arm/current.h	Tue Jan 20 16:39:42 1998
+++ linux/include/asm-arm/current.h	Thu Jun 17 01:11:35 1999
@@ -4,17 +4,19 @@
 static inline unsigned long get_sp(void)
 {
 	unsigned long sp;
-	__asm__ ("mov %0,sp" : "=r" (sp));
+	__asm__ ("mov	%0,sp" : "=r" (sp));
 	return sp;
 }
 
+//static inline struct task_struct *get_current(void) __attribute__ (( __const__ ));
+
 static inline struct task_struct *get_current(void)
 {
 	struct task_struct *ts;
-	__asm__ __volatile__("
-	bic	%0, sp, #0x1f00
-	bic	%0, %0, #0x00ff
-	" : "=r" (ts));
+	__asm__ __volatile__ (
+	"bic	%0, sp, #0x1f00		@ get_current
+	bic	%0, %0, #0x00ff" 
+	: "=r" (ts));
 	return ts;
 }
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/dma.h linux/include/asm-arm/dma.h
--- v2.3.6/linux/include/asm-arm/dma.h	Sat May  8 11:07:16 1999
+++ linux/include/asm-arm/dma.h	Thu Jun 17 01:11:35 1999
@@ -119,6 +119,10 @@
  */
 extern void set_dma_mode(dmach_t channel, dmamode_t mode);
 
+/* Set the transfer speed for this channel
+ */
+extern void set_dma_speed(dmach_t channel, int cycle_ns);
+
 /* Get DMA residue count. After a DMA transfer, this
  * should return zero. Reading this while a DMA transfer is
  * still in progress will return unpredictable results.
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/ide.h linux/include/asm-arm/ide.h
--- v2.3.6/linux/include/asm-arm/ide.h	Thu May 13 11:00:08 1999
+++ linux/include/asm-arm/ide.h	Thu Jun 17 01:11:35 1999
@@ -46,6 +46,13 @@
 #define ide_release_lock(lock)		do {} while (0)
 #define ide_get_lock(lock, hdlr, data)	do {} while (0)
 
+/*
+ * We always use the new IDE port registering,
+ * so these are fixed here.
+ */
+#define ide_default_io_base(i)		((ide_ioreg_t)0)
+#define ide_default_irq(b)		(0)
+
 #endif /* __KERNEL__ */
 
 #endif /* __ASMARM_IDE_H */
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/io.h linux/include/asm-arm/io.h
--- v2.3.6/linux/include/asm-arm/io.h	Thu May 13 11:00:08 1999
+++ linux/include/asm-arm/io.h	Thu Jun 17 01:11:35 1999
@@ -189,21 +189,10 @@
 #define inl_p(port) __inl_p((port))
 #endif
 
-/* Nothing to do */
-
-#ifndef dma_cache_inv
-#define dma_cache_inv(_start,_size)		do { } while (0)
-#endif
-#ifndef dma_cache_wback
-#define dma_cache_wback(_start,_size)		do { } while (0)
-#ifndef ARCH_READWRITE
-#ifndef dma_cache_wback_inv
-#define dma_cache_wback_inv(_start,_size)	do { } while (0)
 #endif
 
-#endif /* __KERNEL__ */
+#ifndef ARCH_READWRITE
 
-#endif /* __ASM_ARM_IO_H */
 /* for panic */
 #include <linux/kernel.h>
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/irq.h linux/include/asm-arm/irq.h
--- v2.3.6/linux/include/asm-arm/irq.h	Sat May  8 11:07:16 1999
+++ linux/include/asm-arm/irq.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/ptrace.h linux/include/asm-arm/proc-armo/ptrace.h
--- v2.3.6/linux/include/asm-arm/proc-armo/ptrace.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/proc-armo/ptrace.h	Thu Jun 17 01:11:35 1999
@@ -44,6 +44,8 @@
 #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 +72,19 @@
  */
 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
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/semaphore.h linux/include/asm-arm/proc-armo/semaphore.h
--- v2.3.6/linux/include/asm-arm/proc-armo/semaphore.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/proc-armo/semaphore.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/system.h linux/include/asm-arm/proc-armo/system.h
--- v2.3.6/linux/include/asm-arm/proc-armo/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/proc-armo/system.h	Thu Jun 17 01:11:35 1999
@@ -110,6 +110,12 @@
 	  : "memory");					\
 	} while (0)
 
+/* For spinlocks etc */
+#define local_irq_save(x)	__save_flags_cli(x)
+#define local_irq_restore(x)	__restore_flags(x)
+#define local_irq_disable()	__cli()
+#define local_irq_enable()	__sti()
+
 #ifdef __SMP__
 #error SMP not supported
 #else
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/ptrace.h linux/include/asm-arm/proc-armv/ptrace.h
--- v2.3.6/linux/include/asm-arm/proc-armv/ptrace.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/ptrace.h	Thu Jun 17 01:11:35 1999
@@ -52,6 +52,8 @@
 #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) || \
@@ -81,8 +83,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;
 
 	/*
@@ -92,6 +94,8 @@
 
 	return 0;
 }
+
+#endif	/* __KERNEL__ */
 
 #endif
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/semaphore.h linux/include/asm-arm/proc-armv/semaphore.h
--- v2.3.6/linux/include/asm-arm/proc-armv/semaphore.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/semaphore.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/system.h linux/include/asm-arm/proc-armv/system.h
--- v2.3.6/linux/include/asm-arm/proc-armv/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/proc-armv/system.h	Thu Jun 17 01:11:35 1999
@@ -121,6 +121,12 @@
 	  : "memory");			\
 	} while (0)
 
+/* For spinlocks etc */
+#define local_irq_save(x)	__save_flags_cli(x)
+#define local_irq_restore(x)	__restore_flags(x)
+#define local_irq_disable()	__cli()
+#define local_irq_enable()	__sti()
+
 #ifdef __SMP__
 #error SMP not supported
 #else
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- v2.3.6/linux/include/asm-arm/processor.h	Tue May 25 14:55:05 1999
+++ linux/include/asm-arm/processor.h	Thu Jun 17 01:11:35 1999
@@ -36,6 +36,7 @@
 
 #define NR_DEBUGS	5
 
+#include <asm/proc/ptrace.h>
 #include <asm/arch/processor.h>
 #include <asm/proc/processor.h>
 
@@ -86,6 +87,7 @@
 }
 
 /* Forward declaration, a strange C thing */
+struct task_struct;
 struct mm_struct;
 
 /* Free all resources held by a thread. */
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/semaphore.h linux/include/asm-arm/semaphore.h
--- v2.3.6/linux/include/asm-arm/semaphore.h	Sat May 15 15:05:37 1999
+++ linux/include/asm-arm/semaphore.h	Thu Jun 17 01:11:35 1999
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/atomic.h>
+#include <linux/wait.h>
 
 struct semaphore {
 	atomic_t count;
@@ -13,8 +14,35 @@
 	wait_queue_head_t wait;
 };
 
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
+#define __SEMAPHORE_INIT(name,count)			\
+	{ ATOMIC_INIT(count), 0,			\
+	  __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) }
+
+#define __MUTEX_INITIALIZER(name) \
+	__SEMAPHORE_INIT(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count)	\
+	struct semaphore name = __SEMAPHORE_INIT(name,count)
+
+#define DECLARE_MUTEX(name)		__DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name)	__DECLARE_SEMAPHORE_GENERIC(name,0)
+
+#define sema_init(sem, val)			\
+do {						\
+	atomic_set(&((sem)->count), (val));	\
+	(sem)->waking = 0;			\
+	init_waitqueue_head(&(sem)->wait);	\
+} while (0)
+
+static inline void init_MUTEX(struct semaphore *sem)
+{
+	sema_init(sem, 1);
+}
+
+static inline void init_MUTEX_LOCKED(struct semaphore *sem)
+{
+	sema_init(sem, 0);
+}
 
 asmlinkage void __down_failed (void /* special register calling convention */);
 asmlinkage int  __down_interruptible_failed (void /* special register calling convention */);
@@ -26,7 +54,7 @@
 extern int  __down_trylock(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
-#define sema_init(sem, val)	atomic_set(&((sem)->count), (val))
+extern spinlock_t semaphore_wake_lock;
 
 #include <asm/proc/semaphore.h>
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/softirq.h linux/include/asm-arm/softirq.h
--- v2.3.6/linux/include/asm-arm/softirq.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/softirq.h	Thu Jun 17 01:11:35 1999
@@ -5,10 +5,18 @@
 #include <asm/hardirq.h>
 
 extern unsigned int local_bh_count[NR_CPUS];
-#define in_bh()			(local_bh_count[smp_processor_id()] != 0)
+
+#define cpu_bh_disable(cpu)	do { local_bh_count[(cpu)]++; barrier(); } while (0)
+#define cpu_bh_enable(cpu)	do { barrier(); local_bh_count[(cpu)]--; } while (0)
+
+#define cpu_bh_trylock(cpu)	(local_bh_count[(cpu)] ? 0 : (local_bh_count[(cpu)] = 1))
+#define cpu_bh_endlock(cpu)	(local_bh_count[(cpu)] = 0)
+
+#define local_bh_disable()	cpu_bh_disable(smp_processor_id())
+#define local_bh_enable()	cpu_bh_enable(smp_processor_id())
 
 #define get_active_bhs()	(bh_mask & bh_active)
-#define clear_active_bhs(x)	atomic_clear_mask((int)(x),&bh_active)
+#define clear_active_bhs(x)	atomic_clear_mask((x),&bh_active)
 
 extern inline void init_bh(int nr, void (*routine)(void))
 {
@@ -19,8 +27,9 @@
 
 extern inline void remove_bh(int nr)
 {
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	mb();
+	bh_base[nr] = NULL;
 }
 
 extern inline void mark_bh(int nr)
@@ -34,20 +43,20 @@
 
 extern inline void start_bh_atomic(void)
 {
-	local_bh_count[smp_processor_id()]++;
+	local_bh_disable();
 	barrier();
 }
 
 extern inline void end_bh_atomic(void)
 {
 	barrier();
-	local_bh_count[smp_processor_id()]--;
+	local_bh_enable();
 }
 
 /* These are for the irq's testing the lock */
-#define softirq_trylock(cpu)	(in_bh() ? 0 : (local_bh_count[smp_processor_id()]=1))
-#define softirq_endlock(cpu)	(local_bh_count[smp_processor_id()] = 0)
-#define synchronize_bh()	do { } while (0)
+#define softirq_trylock(cpu)	(cpu_bh_trylock(cpu))
+#define softirq_endlock(cpu)	(cpu_bh_endlock(cpu))
+#define synchronize_bh()	barrier()
 
 #endif	/* SMP */
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/spinlock.h linux/include/asm-arm/spinlock.h
--- v2.3.6/linux/include/asm-arm/spinlock.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/spinlock.h	Thu Jun 17 01:11:35 1999
@@ -1,39 +1,96 @@
 #ifndef __ASM_SPINLOCK_H
 #define __ASM_SPINLOCK_H
 
-#ifndef __SMP__
-
 /*
  * To be safe, we assume the only compiler that can cope with
  * empty initialisers is EGCS.
  */
 #if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 90))
-#define EMPTY_INIT_OK
+#define EMPTY_STRUCT		struct { }
+#define EMPTY_STRUCT_INIT(t)	(t) { }
+#else
+#define EMPTY_STRUCT		unsigned char
+#define EMPTY_STRUCT_INIT(t)	(t) 0
 #endif
 
 /*
+ * These are the generic versions of the spinlocks
+ * and read-write locks.. We should actually do a
+ * <linux/spinlock.h> with all of this. Oh, well.
+ */
+#define spin_lock_irqsave(lock, flags)		do { local_irq_save(flags);       spin_lock(lock); } while (0)
+#define spin_lock_irq(lock)			do { local_irq_disable();         spin_lock(lock); } while (0)
+#define spin_lock_bh(lock)			do { local_bh_disable();          spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags)		do { local_irq_save(flags);       read_lock(lock); } while (0)
+#define read_lock_irq(lock)			do { local_irq_disable();         read_lock(lock); } while (0)
+#define read_lock_bh(lock)			do { local_bh_disable();          read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags)		do { local_irq_save(flags);      write_lock(lock); } while (0)
+#define write_lock_irq(lock)			do { local_irq_disable();        write_lock(lock); } while (0)
+#define write_lock_bh(lock)			do { local_bh_disable();         write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags)	do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
+#define spin_unlock_irq(lock)			do { spin_unlock(lock);  local_irq_enable();       } while (0)
+#define spin_unlock_bh(lock)			do { spin_unlock(lock);  local_bh_enable();        } while (0)
+
+#define read_unlock_irqrestore(lock, flags)	do { read_unlock(lock);  local_irq_restore(flags); } while (0)
+#define read_unlock_irq(lock)			do { read_unlock(lock);  local_irq_enable();       } while (0)
+#define read_unlock_bh(lock)			do { read_unlock(lock);  local_bh_enable();        } while (0)
+
+#define write_unlock_irqrestore(lock, flags)	do { write_unlock(lock); local_irq_restore(flags); } while (0)
+#define write_unlock_irq(lock)			do { write_unlock(lock); local_irq_enable();       } while (0)
+#define write_unlock_bh(lock)			do { write_unlock(lock); local_bh_enable();        } while (0)
+
+#ifndef __SMP__
+
+#define DEBUG_SPINLOCKS 0	/* 0 == no debugging, 1 == maintain lock state, 2 == full debugging */
+
+#if (DEBUG_SPINLOCKS < 1)
+/*
  * Your basic spinlocks, allowing only a single CPU anywhere
  */
-#ifdef EMPTY_INIT_OK
-  typedef struct { } spinlock_t;
-# define SPIN_LOCK_UNLOCKED (spinlock_t) { }
-#else
-  typedef unsigned char spinlock_t;
-# define SPIN_LOCK_UNLOCKED 0
-#endif
+typedef EMPTY_STRUCT spinlock_t;
+#define SPIN_LOCK_UNLOCKED EMPTY_STRUCT_INIT(spinlock_t)
 
 #define spin_lock_init(lock)	do { } while(0)
 #define spin_lock(lock)		do { } while(0)
-#define spin_trylock(lock)	do { } while(0)
+#define spin_trylock(lock)	(1)
 #define spin_unlock_wait(lock)	do { } while(0)
 #define spin_unlock(lock)	do { } while(0)
-#define spin_lock_irq(lock)	cli()
-#define spin_unlock_irq(lock)	sti()
 
-#define spin_lock_irqsave(lock, flags) \
-	do { __save_flags_cli(flags); } while (0)
-#define spin_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
+#elif (DEBUG_SPINLOCKS < 2)
+
+typedef struct {
+	volatile unsigned int lock;
+} spinlock_t;
+#define SPIN_LOCK_UNLOCKED (pinlock_t) { 0 }
+
+#define spin_lock_init(x)	do { (x)->lock = 0; } while (0)
+#define spin_lock(x)		do { (x)->lock = 1; } while (0)
+#define spin_trylock(lock)	(!test_and_set_bit(0,(lock)))
+#define spin_unlock_wait(x)	do { } while (0)
+#define spin_unlock(x)		do { (x)->lock = 0; } while (0)
+
+#else /* (DEBUG_SPINLOCKS >= 2) */
+
+typedef struct {
+	volatule unsigned int lock;
+	volatile unsigned int babble;
+	const char *module;
+} spinlock_t;
+#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0, 25, __BASE_FILE__ }
+
+#include <linux/kernel.h>
+
+#define spin_lock_init(x)	do { (x)->lock = 0; } while (0)
+#define spin_trylock(lock)	(!test_and_set_bit(0,(lock)))
+
+#define spin_lock(x)		do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
+#define spin_unlock_wait(x)	do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
+#define spin_unlock(x)		do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
+
+#endif
 
 /*
  * Read-write spinlocks, allowing multiple readers
@@ -45,31 +102,13 @@
  * irq-safe write-lock, but readers can get non-irqsafe
  * read-locks.
  */
-#ifdef EMPTY_INIT_OK
-  typedef struct { } rwlock_t;
-# define RW_LOCK_UNLOCKED (rwlock_t) { }
-#else
-  typedef unsigned char rwlock_t;
-# define RW_LOCK_UNLOCKED 0
-#endif
+typedef EMPTY_STRUCT rwlock_t;
+#define RW_LOCK_UNLOCKED EMPTY_STRUCT_INIT(rwlock_t)
 
 #define read_lock(lock)		do { } while(0)
 #define read_unlock(lock)	do { } while(0)
 #define write_lock(lock)	do { } while(0)
 #define write_unlock(lock)	do { } while(0)
-#define read_lock_irq(lock)	cli()
-#define read_unlock_irq(lock)	sti()
-#define write_lock_irq(lock)	cli()
-#define write_unlock_irq(lock)	sti()
-
-#define read_lock_irqsave(lock, flags)	\
-	do { __save_flags_cli(flags); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
-#define write_lock_irqsave(lock, flags)	\
-	do { __save_flags_cli(flags); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
 
 #else
 #error ARM architecture does not support spin locks
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/system.h linux/include/asm-arm/system.h
--- v2.3.6/linux/include/asm-arm/system.h	Sat May  8 11:07:16 1999
+++ linux/include/asm-arm/system.h	Thu Jun 17 01:11:35 1999
@@ -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,28 +118,16 @@
 #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))))
 
 #define tas(ptr) (xchg((ptr),1))
 
-/*
- * 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
@@ -152,7 +140,16 @@
 #define wmb() mb()
 #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t");
 
-extern asmlinkage void __backtrace(void);
+/*
+ * switch_to(prev, next) should switch from task `prev' to `next'
+ * `prev' will never be the same as `next'.
+ * The `mb' is to tell GCC not to cache `current' across this call.
+ */
+#define switch_to(prev,next,last)			\
+	do {			 			\
+		last = processor._switch_to(prev,next);	\
+		mb();					\
+	} while (0)
 
 #endif
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h
--- v2.3.6/linux/include/asm-arm/unistd.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/unistd.h	Thu Jun 17 01:11:35 1999
@@ -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 -u --recursive --new-file v2.3.6/linux/include/asm-i386/page.h linux/include/asm-i386/page.h
--- v2.3.6/linux/include/asm-i386/page.h	Tue Jun  8 23:03:39 1999
+++ linux/include/asm-i386/page.h	Sun Jun 20 17:46:13 1999
@@ -84,6 +84,19 @@
 
 #define __PAGE_OFFSET		(PAGE_OFFSET_RAW)
 
+#ifndef __ASSEMBLY__
+
+#define BUG() do { \
+	printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
+	__asm__ __volatile__(".byte 0x0f,0x0b"); \
+} while (0)
+
+#define PAGE_BUG(page) do { \
+	BUG(); \
+} while (0)
+
+#endif /* __ASSEMBLY__ */
+
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
diff -u --recursive --new-file v2.3.6/linux/include/asm-i386/smplock.h linux/include/asm-i386/smplock.h
--- v2.3.6/linux/include/asm-i386/smplock.h	Tue Jun  8 23:03:41 1999
+++ linux/include/asm-i386/smplock.h	Sun Jun 20 17:46:15 1999
@@ -49,6 +49,8 @@
 
 extern __inline__ void unlock_kernel(void)
 {
+	if (current->lock_depth < 0)
+		BUG();
 	__asm__ __volatile__(
 		"decl %1\n\t"
 		"jns 9f\n\t"
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h
--- v2.3.6/linux/include/asm-sparc/namei.h	Wed Jun  9 16:24:15 1999
+++ linux/include/asm-sparc/namei.h	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.13 1999/04/06 06:54:36 jj Exp $
+/* $Id: namei.h,v 1.14 1999/06/10 05:23:12 davem Exp $
  * linux/include/asm-sparc/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.3.6/linux/include/asm-sparc/page.h	Wed Mar 10 16:53:37 1999
+++ linux/include/asm-sparc/page.h	Thu Jun 17 01:08:50 1999
@@ -28,6 +28,10 @@
 
 #ifndef __ASSEMBLY__
 
+#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0)
+#define PAGE_BUG(page) do { \
+				BUG(); } while (0)
+
 #define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE)
 #define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/spinlock.h linux/include/asm-sparc/spinlock.h
--- v2.3.6/linux/include/asm-sparc/spinlock.h	Thu May 27 09:55:22 1999
+++ linux/include/asm-sparc/spinlock.h	Thu Jun 17 01:08:50 1999
@@ -17,7 +17,7 @@
 
 #define spin_lock_init(lock)	do { } while(0)
 #define spin_lock(lock)		do { } while(0)
-#define spin_trylock(lock)	do { } while(0)
+#define spin_trylock(lock)	(1)
 #define spin_unlock_wait(lock)	do { } while(0)
 #define spin_unlock(lock)	do { } while(0)
 #define spin_lock_irq(lock)	cli()
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- v2.3.6/linux/include/asm-sparc64/elf.h	Sun Oct  4 10:22:44 1998
+++ linux/include/asm-sparc64/elf.h	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.18 1998/09/09 05:36:08 davem Exp $ */
+/* $Id: elf.h,v 1.19 1999/06/11 13:26:04 jj Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
@@ -7,7 +7,9 @@
  */
 
 #include <asm/ptrace.h>
+#ifdef __KERNEL__
 #include <asm/processor.h>
+#endif
 
 /*
  * These are used to set parameters in the core dumps.
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h
--- v2.3.6/linux/include/asm-sparc64/namei.h	Wed Jun  9 16:24:15 1999
+++ linux/include/asm-sparc64/namei.h	Thu Jun 17 01:08:50 1999
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.14 1999/04/06 06:54:39 jj Exp $
+/* $Id: namei.h,v 1.15 1999/06/10 05:23:17 davem Exp $
  * linux/include/asm-sparc64/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.3.6/linux/include/asm-sparc64/page.h	Tue Oct 27 09:52:21 1998
+++ linux/include/asm-sparc64/page.h	Thu Jun 17 01:08:50 1999
@@ -18,6 +18,10 @@
 
 #ifndef __ASSEMBLY__
 
+#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0)
+#define PAGE_BUG(page) do { \
+				BUG(); } while (0)
+
 extern void clear_page(unsigned long page);
 extern void copy_page(unsigned long to, unsigned long from);
 
diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/spinlock.h linux/include/asm-sparc64/spinlock.h
--- v2.3.6/linux/include/asm-sparc64/spinlock.h	Tue May 25 13:06:34 1999
+++ linux/include/asm-sparc64/spinlock.h	Thu Jun 17 01:08:50 1999
@@ -15,7 +15,7 @@
 
 #define spin_lock_init(lock)	do { } while(0)
 #define spin_lock(lock)		do { } while(0)
-#define spin_trylock(lock)	do { } while(0)
+#define spin_trylock(lock)	(1)
 #define spin_unlock_wait(lock)	do { } while(0)
 #define spin_unlock(lock)	do { } while(0)
 #define spin_lock_irq(lock)	cli()
diff -u --recursive --new-file v2.3.6/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.3.6/linux/include/linux/blk.h	Tue Jun  8 23:04:28 1999
+++ linux/include/linux/blk.h	Sun Jun 20 17:47:10 1999
@@ -342,6 +342,15 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == MFM_ACORN_MAJOR)
+
+#define DEVICE_NAME "mfm disk"
+#define DEVICE_INTR do_mfm
+#define DEVICE_REQUEST do_mfm_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #elif (MAJOR_NR == NBD_MAJOR)
 
 #define DEVICE_NAME "nbd"
diff -u --recursive --new-file v2.3.6/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v2.3.6/linux/include/linux/ext2_fs.h	Tue May 11 14:37:47 1999
+++ linux/include/linux/ext2_fs.h	Wed Jun 16 19:26:27 1999
@@ -556,6 +556,7 @@
 extern int ext2_bmap (struct inode *, int);
 
 extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
+extern int ext2_getblk_block (struct inode *, long, int, int *, int *);
 extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
 
 extern int ext2_getcluster (struct inode * inode, long block);
diff -u --recursive --new-file v2.3.6/linux/include/linux/fd1772.h linux/include/linux/fd1772.h
--- v2.3.6/linux/include/linux/fd1772.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/fd1772.h	Thu Jun 17 01:11:35 1999
@@ -0,0 +1,80 @@
+#ifndef _LINUX_FD1772REG_H
+#define _LINUX_FD1772REG_H
+
+/*
+** WD1772 stuff - originally from the M68K Linux
+ * Modified for Archimedes by Dave Gilbert (gilbertd@cs.man.ac.uk)
+ */
+
+/* register codes */
+
+#define FDC1772SELREG_STP   (0x80)   /* command/status register */
+#define FDC1772SELREG_TRA   (0x82)   /* track register */
+#define FDC1772SELREG_SEC   (0x84)   /* sector register */
+#define FDC1772SELREG_DTA   (0x86)   /* data register */
+
+/* register names for FDC1772_READ/WRITE macros */
+
+#define FDC1772REG_CMD         0
+#define FDC1772REG_STATUS      0
+#define FDC1772REG_TRACK       2
+#define FDC1772REG_SECTOR      4
+#define FDC1772REG_DATA                6
+
+/* command opcodes */
+
+#define FDC1772CMD_RESTORE  (0x00)   /*  -                   */
+#define FDC1772CMD_SEEK     (0x10)   /*   |                  */
+#define FDC1772CMD_STEP     (0x20)   /*   |  TYP 1 Commands  */
+#define FDC1772CMD_STIN     (0x40)   /*   |                  */
+#define FDC1772CMD_STOT     (0x60)   /*  -                   */
+#define FDC1772CMD_RDSEC    (0x80)   /*  -   TYP 2 Commands  */
+#define FDC1772CMD_WRSEC    (0xa0)   /*  -          "        */
+#define FDC1772CMD_RDADR    (0xc0)   /*  -                   */
+#define FDC1772CMD_RDTRA    (0xe0)   /*   |  TYP 3 Commands  */
+#define FDC1772CMD_WRTRA    (0xf0)   /*  -                   */
+#define FDC1772CMD_FORCI    (0xd0)   /*  -   TYP 4 Command   */
+
+/* command modifier bits */
+
+#define FDC1772CMDADD_SR6   (0x00)   /* step rate settings */
+#define FDC1772CMDADD_SR12  (0x01)
+#define FDC1772CMDADD_SR2   (0x02)
+#define FDC1772CMDADD_SR3   (0x03)
+#define FDC1772CMDADD_V     (0x04)   /* verify */
+#define FDC1772CMDADD_H     (0x08)   /* wait for spin-up */
+#define FDC1772CMDADD_U     (0x10)   /* update track register */
+#define FDC1772CMDADD_M     (0x10)   /* multiple sector access */
+#define FDC1772CMDADD_E     (0x04)   /* head settling flag */
+#define FDC1772CMDADD_P     (0x02)   /* precompensation */
+#define FDC1772CMDADD_A0    (0x01)   /* DAM flag */
+
+/* status register bits */
+
+#define        FDC1772STAT_MOTORON     (0x80)   /* motor on */
+#define        FDC1772STAT_WPROT       (0x40)   /* write protected (FDC1772CMD_WR*) */
+#define        FDC1772STAT_SPINUP      (0x20)   /* motor speed stable (Type I) */
+#define        FDC1772STAT_DELDAM      (0x20)   /* sector has deleted DAM (Type II+III) */
+#define        FDC1772STAT_RECNF       (0x10)   /* record not found */
+#define        FDC1772STAT_CRC         (0x08)   /* CRC error */
+#define        FDC1772STAT_TR00        (0x04)   /* Track 00 flag (Type I) */
+#define        FDC1772STAT_LOST        (0x04)   /* Lost Data (Type II+III) */
+#define        FDC1772STAT_IDX         (0x02)   /* Index status (Type I) */
+#define        FDC1772STAT_DRQ         (0x02)   /* DRQ status (Type II+III) */
+#define        FDC1772STAT_BUSY        (0x01)   /* FDC1772 is busy */
+
+
+/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1  1 -> Side 2 */
+#define DSKSIDE     (0x01)
+        
+#define DSKDRVNONE  (0x06)
+#define DSKDRV0     (0x02)
+#define DSKDRV1     (0x04)
+
+/* step rates */
+#define        FDC1772STEP_6   0x00
+#define        FDC1772STEP_12  0x01
+#define        FDC1772STEP_2   0x02
+#define        FDC1772STEP_3   0x03
+
+#endif
diff -u --recursive --new-file v2.3.6/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.6/linux/include/linux/fs.h	Tue Jun  8 23:03:41 1999
+++ linux/include/linux/fs.h	Sun Jun 20 17:46:16 1999
@@ -74,11 +74,11 @@
 
 /* public flags for file_system_type */
 #define FS_REQUIRES_DEV 1 
-#define FS_NO_DCACHE    2 /* Only dcache the necessary things. */
-#define FS_NO_PRELIM    4 /* prevent preloading of dentries, even if
+#define FS_NO_DCACHE	2 /* Only dcache the necessary things. */
+#define FS_NO_PRELIM	4 /* prevent preloading of dentries, even if
 			   * FS_NO_DCACHE is not set.
 			   */
-#define FS_IBASKET      8 /* FS does callback to free_ibasket() if space gets low. */
+#define FS_IBASKET	8 /* FS does callback to free_ibasket() if space gets low. */
 
 /*
  * These are the fs-independent mount-flags: up to 16 flags are supported
@@ -94,9 +94,9 @@
 #define S_APPEND	256	/* Append-only file */
 #define S_IMMUTABLE	512	/* Immutable file */
 #define MS_NOATIME	1024	/* Do not update access times. */
-#define MS_NODIRATIME   2048    /* Do not update directory access times */
+#define MS_NODIRATIME	2048	/* Do not update directory access times */
 
-#define MS_ODD_RENAME   32768    /* Temporary stuff; will go away as soon
+#define MS_ODD_RENAME	32768	/* Temporary stuff; will go away as soon
 				  * as nfs_rename() will be cleaned up
 				  */
 
@@ -189,7 +189,6 @@
 #define BH_Lock		2	/* 1 if the buffer is locked */
 #define BH_Req		3	/* 0 if the buffer has been invalidated */
 #define BH_Protected	6	/* 1 if the buffer is protected */
-
 /*
  * Try to keep the most commonly used fields in single cache lines (16
  * bytes) to improve performance.  This ordering should be
@@ -218,7 +217,7 @@
 	/* Non-performance-critical data follows. */
 	char * b_data;			/* pointer to data block (1024 bytes) */
 	unsigned int b_list;		/* List that this buffer appears */
-	unsigned long b_flushtime;      /* Time when this (dirty) buffer
+	unsigned long b_flushtime;	/* Time when this (dirty) buffer
 					 * should be written */
 	wait_queue_head_t b_wait;
 	struct buffer_head ** b_pprev;		/* doubly linked list of hash-queue */
@@ -235,30 +234,13 @@
 typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
 void init_buffer(struct buffer_head *, kdev_t, int, bh_end_io_t *, void *);
 
-static inline int buffer_uptodate(struct buffer_head * bh)
-{
-	return test_bit(BH_Uptodate, &bh->b_state);
-}	
-
-static inline int buffer_dirty(struct buffer_head * bh)
-{
-	return test_bit(BH_Dirty, &bh->b_state);
-}
+#define __buffer_state(bh, state)	(((bh)->b_state & (1UL << BH_##state)) != 0)
 
-static inline int buffer_locked(struct buffer_head * bh)
-{
-	return test_bit(BH_Lock, &bh->b_state);
-}
-
-static inline int buffer_req(struct buffer_head * bh)
-{
-	return test_bit(BH_Req, &bh->b_state);
-}
-
-static inline int buffer_protected(struct buffer_head * bh)
-{
-	return test_bit(BH_Protected, &bh->b_state);
-}
+#define buffer_uptodate(bh)	__buffer_state(bh,Uptodate)
+#define buffer_dirty(bh)	__buffer_state(bh,Dirty)
+#define buffer_locked(bh)	__buffer_state(bh,Lock)
+#define buffer_req(bh)		__buffer_state(bh,Req)
+#define buffer_protected(bh)	__buffer_state(bh,Protected)
 
 #define buffer_page(bh)		(mem_map + MAP_NR((bh)->b_data))
 #define touch_buffer(bh)	set_bit(PG_referenced, &buffer_page(bh)->flags)
@@ -357,7 +339,6 @@
 	unsigned long		i_version;
 	unsigned long		i_nrpages;
 	struct semaphore	i_sem;
-	struct semaphore	i_atomic_write;
 	struct inode_operations	*i_op;
 	struct super_block	*i_sb;
 	wait_queue_head_t	i_wait;
@@ -365,22 +346,21 @@
 	struct vm_area_struct	*i_mmap;
 	struct page		*i_pages;
 	struct dquot		*i_dquot[MAXQUOTAS];
+	struct pipe_inode_info	*i_pipe;
 
 	unsigned long		i_state;
 
 	unsigned int		i_flags;
-	unsigned char		i_pipe;
 	unsigned char		i_sock;
 
 	int			i_writecount;
 	unsigned int		i_attr_flags;
 	__u32			i_generation;
 	union {
-		struct pipe_inode_info		pipe_i;
 		struct minix_inode_info		minix_i;
 		struct ext2_inode_info		ext2_i;
 		struct hpfs_inode_info		hpfs_i;
-		struct ntfs_inode_info          ntfs_i;
+		struct ntfs_inode_info		ntfs_i;
 		struct msdos_inode_info		msdos_i;
 		struct umsdos_inode_info	umsdos_i;
 		struct iso_inode_info		isofs_i;
@@ -388,13 +368,13 @@
 		struct sysv_inode_info		sysv_i;
 		struct affs_inode_info		affs_i;
 		struct ufs_inode_info		ufs_i;
-		struct efs_inode_info		efs_i;	   
+		struct efs_inode_info		efs_i;
 		struct romfs_inode_info		romfs_i;
 		struct coda_inode_info		coda_i;
 		struct smb_inode_info		smbfs_i;
 		struct hfs_inode_info		hfs_i;
 		struct adfs_inode_info		adfs_i;
-		struct qnx4_inode_info		qnx4_i;	   
+		struct qnx4_inode_info		qnx4_i;
 		struct socket			socket_i;
 		void				*generic_ip;
 	} u;
@@ -491,10 +471,10 @@
 extern void posix_unblock_lock(struct file_lock *);
 
 struct fasync_struct {
-	int    magic;
-	int    fa_fd;
-	struct fasync_struct	*fa_next; /* singly linked list */
-	struct file 		*fa_file;
+	int	magic;
+	int	fa_fd;
+	struct	fasync_struct	*fa_next; /* singly linked list */
+	struct	file 		*fa_file;
 };
 
 #define FASYNC_MAGIC 0x4601
@@ -547,19 +527,19 @@
 		struct minix_sb_info	minix_sb;
 		struct ext2_sb_info	ext2_sb;
 		struct hpfs_sb_info	hpfs_sb;
-		struct ntfs_sb_info     ntfs_sb;
+		struct ntfs_sb_info	ntfs_sb;
 		struct msdos_sb_info	msdos_sb;
 		struct isofs_sb_info	isofs_sb;
 		struct nfs_sb_info	nfs_sb;
 		struct sysv_sb_info	sysv_sb;
 		struct affs_sb_info	affs_sb;
 		struct ufs_sb_info	ufs_sb;
-		struct efs_sb_info	efs_sb;	   
+		struct efs_sb_info	efs_sb;
 		struct romfs_sb_info	romfs_sb;
 		struct smb_sb_info	smbfs_sb;
 		struct hfs_sb_info	hfs_sb;
 		struct adfs_sb_info	adfs_sb;
-		struct qnx4_sb_info	qnx4_sb;	   
+		struct qnx4_sb_info	qnx4_sb;
 		void			*generic_sbp;
 	} u;
 	/*
@@ -616,13 +596,22 @@
 			struct inode *, struct dentry *);
 	int (*readlink) (struct dentry *, char *,int);
 	struct dentry * (*follow_link) (struct dentry *, struct dentry *, unsigned int);
+	/*
+	 * the order of these functions within the VFS template has been
+	 * changed because SMP locking has changed: from now on all bmap,
+	 * readpage, writepage and flushpage functions are supposed to do
+	 * whatever locking they need to get proper SMP operation - for
+	 * now in most cases this means a lock/unlock_kernel at entry/exit.
+	 * [The new order is also slightly more logical :)]
+	 */
+	int (*bmap) (struct inode *,int);
 	int (*readpage) (struct file *, struct page *);
 	int (*writepage) (struct file *, struct page *);
-	int (*bmap) (struct inode *,int);
+	int (*flushpage) (struct inode *, struct page *, unsigned long);
+
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int);
 	int (*smap) (struct inode *,int);
-	int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int);
 	int (*revalidate) (struct dentry *);
 };
 
@@ -749,13 +738,11 @@
 
 extern struct file *inuse_filps;
 
-extern void refile_buffer(struct buffer_head *);
 extern void set_writetime(struct buffer_head *, int);
 extern int try_to_free_buffers(struct page *);
+extern void refile_buffer(struct buffer_head * buf);
 
-extern int nr_buffers;
 extern int buffermem;
-extern int nr_buffer_heads;
 
 #define BUF_CLEAN	0
 #define BUF_LOCKED	1	/* Buffers scheduled for write */
@@ -766,21 +753,36 @@
 
 extern inline void mark_buffer_clean(struct buffer_head * bh)
 {
-	if (test_and_clear_bit(BH_Dirty, &bh->b_state)) {
-		if (bh->b_list == BUF_DIRTY)
-			refile_buffer(bh);
-	}
+	if (test_and_clear_bit(BH_Dirty, &bh->b_state))
+		refile_buffer(bh);
 }
 
+extern void FASTCALL(__mark_buffer_dirty(struct buffer_head *bh, int flag));
+extern void FASTCALL(__atomic_mark_buffer_dirty(struct buffer_head *bh, int flag));
+
+#define atomic_set_buffer_dirty(bh) test_and_set_bit(BH_Dirty, &(bh)->b_state)
+
 extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag)
 {
-	if (!test_and_set_bit(BH_Dirty, &bh->b_state)) {
-		set_writetime(bh, flag);
-		if (bh->b_list != BUF_DIRTY)
-			refile_buffer(bh);
-	}
+	if (!atomic_set_buffer_dirty(bh))
+		__mark_buffer_dirty(bh, flag);
+}
+
+/*
+ * SMP-safe version of the above - does synchronization with
+ * other users of buffer-cache data structures.
+ *
+ * since we test-set the dirty bit in a CPU-atomic way we also
+ * have optimized the common 'redirtying' case away completely.
+ */
+extern inline void atomic_mark_buffer_dirty(struct buffer_head * bh, int flag)
+{
+	if (!atomic_set_buffer_dirty(bh))
+		__atomic_mark_buffer_dirty(bh, flag);
 }
 
+
+extern void balance_dirty(kdev_t);
 extern int check_disk_change(kdev_t);
 extern int invalidate_inodes(struct super_block *);
 extern void invalidate_inode_pages(struct inode *);
@@ -869,12 +871,19 @@
 extern int brw_page(int, struct page *, kdev_t, int [], int, int);
 
 typedef long (*writepage_t)(struct file *, struct page *, unsigned long, unsigned long, const char *);
+typedef int (*fs_getblock_t)(struct inode *, long, int, int *, int *);
+
+/* Generic buffer handling for block filesystems.. */
+extern int block_read_full_page(struct file *, struct page *);
+extern int block_write_full_page (struct file *, struct page *, fs_getblock_t);
+extern int block_write_partial_page (struct file *, struct page *, unsigned long, unsigned long, const char *, fs_getblock_t);
+extern int block_flushpage(struct inode *, struct page *, unsigned long);
 
-extern int generic_readpage(struct file *, struct page *);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t);
 
+
 extern struct super_block *get_super(kdev_t);
 extern void put_super(kdev_t);
 unsigned long generate_cluster(kdev_t, int b[], int);
@@ -898,6 +907,7 @@
 
 extern int block_fsync(struct file *, struct dentry *);
 extern int file_fsync(struct file *, struct dentry *);
+extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end);
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
diff -u --recursive --new-file v2.3.6/linux/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h
--- v2.3.6/linux/include/linux/hpfs_fs_i.h	Fri May 14 18:30:46 1999
+++ linux/include/linux/hpfs_fs_i.h	Wed Jun 16 19:26:27 1999
@@ -1,17 +1,7 @@
 #ifndef _HPFS_FS_I
 #define _HPFS_FS_I
 
-#if ANALWARNINGS
-#warning Fix the FIFO stuff!
-#warning Fix the FIFO stuff!
-#warning Fix the FIFO stuff!
-#endif
-
 struct hpfs_inode_info {
-	union {				    /* Linux sometimes destroys this structure */
-		struct pipe_inode_info bla; /* due to a bug. Linus doesn't want to fix */
-		struct socket ble;	    /* it so I had to write this workaround :-) */
-	} dummy;
 	ino_t i_parent_dir;	/* (directories) gives fnode of parent dir */
 	unsigned i_dno;		/* (directories) root dnode */
 	unsigned i_dpos;	/* (directories) temp for readdir */
diff -u --recursive --new-file v2.3.6/linux/include/linux/ioport.h linux/include/linux/ioport.h
--- v2.3.6/linux/include/linux/ioport.h	Wed Oct 22 08:31:55 1997
+++ linux/include/linux/ioport.h	Fri Jun 18 12:43:41 1999
@@ -1,14 +1,38 @@
 /*
- * portio.h	Definitions of routines for detecting, reserving and
+ * ioport.h	Definitions of routines for detecting, reserving and
  *		allocating system resources.
  *
- * Version:	0.01	8/30/93
- *
- * Author:	Donald Becker (becker@super.org)
+ * Authors:	Donald Becker (becker@cesdis.gsfc.nasa.gov)
+ *		David Hinds (dhinds@zen.stanford.edu)
  */
 
-#ifndef _LINUX_PORTIO_H
-#define _LINUX_PORTIO_H
+#ifndef _LINUX_IOPORT_H
+#define _LINUX_IOPORT_H
+
+#define RES_IO		0
+#define RES_MEM		1
+
+extern void reserve_setup(char *str, int *ints);
+
+extern struct resource_entry *iolist, *memlist;
+
+extern int get_resource_list(int class, char *buf);
+extern int check_resource(int class,
+			  unsigned long from, unsigned long extent);
+extern void request_resource(int class,
+			     unsigned long from, unsigned long extent,
+			     const char *name);
+extern void release_resource(int class,
+			     unsigned long from, unsigned long extent);
+extern unsigned long occupy_resource(int class,
+				     unsigned long base, unsigned long end,
+				     unsigned long num, unsigned long align,
+				     const char *name);
+extern void vacate_resource(int class,
+			    unsigned long from, unsigned long extent);
+
+#define get_ioport_list(buf)	get_resource_list(RES_IO, buf)
+#define get_mem_list(buf)	get_resource_list(RES_MEM, buf)
 
 #define HAVE_PORTRESERVE
 /*
@@ -16,20 +40,21 @@
  * Once you have found you hardware, register it with request_region().
  * If you unload the driver, use release_region to free ports.
  */
-extern void reserve_setup(char *str, int *ints);
-extern int check_region(unsigned long from, unsigned long extent);
-extern void request_region(unsigned long from, unsigned long extent,const char *name);
-extern void release_region(unsigned long from, unsigned long extent);
-extern int get_ioport_list(char *);
-
-#ifdef __sparc__
-extern unsigned long occupy_region(unsigned long base, unsigned long end,
-				   unsigned long num, unsigned int align,
-				   const char *name);
-#endif
+#define check_region(f,e)		check_resource(RES_IO,f,e)
+#define request_region(f,e,n)		request_resource(RES_IO,f,e,n)
+#define release_region(f,e)		release_resource(RES_IO,f,e)
+#define occupy_region(b,e,n,a,s)	occupy_resource(RES_IO,b,e,n,a,s)
+#define vacate_region(f,e)		vacate_resource(RES_IO,f,e)
+
+#define HAVE_MEMRESERVE
+#define check_mem_region(f,e)		check_resource(RES_MEM,f,e)
+#define request_mem_region(f,e,n)	request_resource(RES_MEM,f,e,n)
+#define release_mem_region(f,e)		release_resource(RES_MEM,f,e)
+#define occupy_mem_region(b,e,n,a,s)	occupy_resource(RES_MEM,b,e,n,a,s)
+#define vacate_mem_region(f,e)		vacate_resource(RES_MEM,f,e)
 
 #define HAVE_AUTOIRQ
 extern void autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 
-#endif	/* _LINUX_PORTIO_H */
+#endif	/* _LINUX_IOPORT_H */
diff -u --recursive --new-file v2.3.6/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v2.3.6/linux/include/linux/minix_fs.h	Fri Apr 23 21:20:38 1999
+++ linux/include/linux/minix_fs.h	Wed Jun 16 19:26:27 1999
@@ -110,6 +110,7 @@
 extern int minix_bmap(struct inode *,int);
 
 extern struct buffer_head * minix_getblk(struct inode *, int, int);
+extern int minix_getblk_block (struct inode *, long, int, int *, int *);
 extern struct buffer_head * minix_bread(struct inode *, int, int);
 
 extern void minix_truncate(struct inode *);
diff -u --recursive --new-file v2.3.6/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.3.6/linux/include/linux/mm.h	Tue Jun  8 23:03:47 1999
+++ linux/include/linux/mm.h	Sun Jun 20 17:46:21 1999
@@ -129,29 +129,57 @@
 	wait_queue_head_t wait;
 	struct page **pprev_hash;
 	struct buffer_head * buffers;
+	int owner; /* temporary debugging check */
 } mem_map_t;
 
+#define get_page(p) do { atomic_inc(&(p)->count); \
+						} while (0)
+#define put_page(p) __free_page(p)
+#define put_page_testzero(p) ({ int __ret = atomic_dec_and_test(&(p)->count);\
+				__ret; })
+#define page_count(p) atomic_read(&(p)->count)
+#define set_page_count(p,v) do { atomic_set(&(p)->count, v); \
+				} while (0)
+
 /* Page flag bit values */
 #define PG_locked		 0
 #define PG_error		 1
 #define PG_referenced		 2
-#define PG_dirty		 3
-#define PG_uptodate		 4
-#define PG_free_after		 5
-#define PG_decr_after		 6
-#define PG_swap_unlock_after	 7
-#define PG_DMA			 8
-#define PG_Slab			 9
-#define PG_swap_cache		10
-#define PG_skip			11
+#define PG_uptodate		 3
+#define PG_free_after		 4
+#define PG_decr_after		 5
+#define PG_swap_unlock_after	 6
+#define PG_DMA			 7
+#define PG_Slab			 8
+#define PG_swap_cache		 9
+#define PG_skip			10
+				/* bits 21-30 unused */
 #define PG_reserved		31
 
+
 /* Make it prettier to test the above... */
+#define Page_Uptodate(page)	(test_bit(PG_uptodate, &(page)->flags))
+#define SetPageUptodate(page)	do { set_bit(PG_uptodate, &(page)->flags); \
+					} while (0)
+#define ClearPageUptodate(page)	do { clear_bit(PG_uptodate, &(page)->flags); \
+					} while (0)
 #define PageLocked(page)	(test_bit(PG_locked, &(page)->flags))
+#define LockPage(page)		\
+	do { int _ret = test_and_set_bit(PG_locked, &(page)->flags); \
+	if (_ret) PAGE_BUG(page); \
+	if (page->owner) PAGE_BUG(page); \
+	page->owner = (int)current; } while (0)
+#define TryLockPage(page)	({ int _ret = test_and_set_bit(PG_locked, &(page)->flags); \
+				if (!_ret) page->owner = (int)current; _ret; })
+#define UnlockPage(page)	do { \
+					if (page->owner != (int)current) { \
+BUG(); } page->owner = 0; \
+if (!test_and_clear_bit(PG_locked, &(page)->flags)) { \
+			PAGE_BUG(page); } wake_up(&page->wait); } while (0)
 #define PageError(page)		(test_bit(PG_error, &(page)->flags))
+#define SetPageError(page)	({ int _ret = test_and_set_bit(PG_error, &(page)->flags); _ret; })
+#define ClearPageError(page)	do { if (!test_and_clear_bit(PG_error, &(page)->flags)) BUG(); } while (0)
 #define PageReferenced(page)	(test_bit(PG_referenced, &(page)->flags))
-#define PageDirty(page)		(test_bit(PG_dirty, &(page)->flags))
-#define PageUptodate(page)	(test_bit(PG_uptodate, &(page)->flags))
 #define PageFreeAfter(page)	(test_bit(PG_free_after, &(page)->flags))
 #define PageDecrAfter(page)	(test_bit(PG_decr_after, &(page)->flags))
 #define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags))
@@ -163,16 +191,12 @@
 #define PageSetSlab(page)	(set_bit(PG_Slab, &(page)->flags))
 #define PageSetSwapCache(page)	(set_bit(PG_swap_cache, &(page)->flags))
 
-#define PageTestandSetDirty(page)	\
-			(test_and_set_bit(PG_dirty, &(page)->flags))
 #define PageTestandSetSwapCache(page)	\
 			(test_and_set_bit(PG_swap_cache, &(page)->flags))
 
 #define PageClearSlab(page)	(clear_bit(PG_Slab, &(page)->flags))
 #define PageClearSwapCache(page)(clear_bit(PG_swap_cache, &(page)->flags))
 
-#define PageTestandClearDirty(page) \
-			(test_and_clear_bit(PG_dirty, &(page)->flags))
 #define PageTestandClearSwapCache(page)	\
 			(test_and_clear_bit(PG_swap_cache, &(page)->flags))
 
@@ -274,8 +298,8 @@
 /* memory.c & swap.c*/
 
 #define free_page(addr) free_pages((addr),0)
-extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
-extern void FASTCALL(__free_page(struct page *));
+extern int FASTCALL(free_pages(unsigned long addr, unsigned long order));
+extern int FASTCALL(__free_page(struct page *));
 
 extern void show_free_areas(void);
 extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
@@ -387,7 +411,7 @@
 
 #define buffer_under_min()	((buffermem >> PAGE_SHIFT) * 100 < \
 				buffer_mem.min_percent * num_physpages)
-#define pgcache_under_min()	(page_cache_size * 100 < \
+#define pgcache_under_min()	(atomic_read(&page_cache_size) * 100 < \
 				page_cache.min_percent * num_physpages)
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.3.6/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h
--- v2.3.6/linux/include/linux/msdos_fs_i.h	Thu May 13 23:18:21 1999
+++ linux/include/linux/msdos_fs_i.h	Wed Jun 16 19:26:27 1999
@@ -1,30 +1,11 @@
 #ifndef _MSDOS_FS_I
 #define _MSDOS_FS_I
 
-#ifndef _LINUX_PIPE_FS_I_H
-#include <linux/pipe_fs_i.h>
-#endif
-
 /*
  * MS-DOS file system inode data in memory
  */
 
 struct msdos_inode_info {
-	/*
-		UMSDOS manage special file and fifo as normal empty
-		msdos file. fifo inode processing conflict with msdos
-		processing. So I insert the pipe_inode_info so the
-		information does not overlap. This increases the size of
-		the msdos_inode_info, but the clear winner here is
-		the ext2_inode_info. So it does not change anything to
-		the total size of a struct inode.
-
-		I have not put it conditional. With the advent of loadable
-		file system drivers, it would be very easy to compile
-		a MS-DOS FS driver unaware of UMSDOS and then later to
-		load a (then incompatible) UMSDOS FS driver.
-	*/
-	struct pipe_inode_info reserved;
 	int i_start;	/* first cluster or 0 */
 	int i_logstart;	/* logical first cluster */
 	int i_attrs;	/* unused attribute bits */
diff -u --recursive --new-file v2.3.6/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.3.6/linux/include/linux/nfs_fs.h	Tue Jun  8 23:03:44 1999
+++ linux/include/linux/nfs_fs.h	Sun Jun 20 17:46:45 1999
@@ -192,7 +192,8 @@
  */
 extern struct inode_operations nfs_dir_inode_operations;
 extern struct dentry_operations nfs_dentry_operations;
-extern void nfs_invalidate_dircache(struct inode *);
+extern void nfs_flush_dircache(struct inode *);
+extern void nfs_free_dircache(struct inode *);
 
 /*
  * linux/fs/nfs/symlink.c
diff -u --recursive --new-file v2.3.6/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- v2.3.6/linux/include/linux/nfs_fs_i.h	Tue Jun  8 22:42:23 1999
+++ linux/include/linux/nfs_fs_i.h	Wed Jun 16 19:26:27 1999
@@ -9,13 +9,6 @@
  */
 struct nfs_inode_info {
 	/*
-	 * This is a place holder so named pipes on NFS filesystems
-	 * work (more or less correctly). This must be first in the
-	 * struct because the data is really accessed via inode->u.pipe_i.
-	 */
-	struct pipe_inode_info	pipeinfo;
-
-	/*
 	 * Various flags
 	 */
 	unsigned short		flags;
diff -u --recursive --new-file v2.3.6/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v2.3.6/linux/include/linux/pagemap.h	Tue Jun  8 23:03:47 1999
+++ linux/include/linux/pagemap.h	Sun Jun 20 17:46:24 1999
@@ -39,10 +39,10 @@
  */
 #define page_cache_entry(x)	(mem_map + MAP_NR(x))
 
-#define PAGE_HASH_BITS 12
+#define PAGE_HASH_BITS 16
 #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
 
-extern unsigned long page_cache_size; /* # of pages currently in the hash table */
+extern atomic_t page_cache_size; /* # of pages currently in the hash table */
 extern struct page * page_hash_table[PAGE_HASH_SIZE];
 
 /*
@@ -64,72 +64,25 @@
 
 #define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))
 
-static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page)
-{
-	goto inside;
-	for (;;) {
-		page = page->next_hash;
-inside:
-		if (!page)
-			goto not_found;
-		if (page->inode != inode)
-			continue;
-		if (page->offset == offset)
-			break;
-	}
-	/* Found the page. */
-	atomic_inc(&page->count);
-	set_bit(PG_referenced, &page->flags);
-not_found:
-	return page;
-}
+extern struct page * __find_get_page (struct inode * inode,
+				unsigned long offset, struct page **hash);
+#define find_get_page(inode, offset) \
+		__find_get_page(inode, offset, page_hash(inode, offset))
+extern struct page * __find_lock_page (struct inode * inode,
+				unsigned long offset, struct page **hash);
+extern void lock_page(struct page *page);
+#define find_lock_page(inode, offset) \
+		__find_lock_page(inode, offset, page_hash(inode, offset))
 
-static inline struct page *find_page(struct inode * inode, unsigned long offset)
-{
-	return __find_page(inode, offset, *page_hash(inode, offset));
-}
+extern void __add_page_to_hash_queue(struct page * page, struct page **p);
 
-static inline void remove_page_from_hash_queue(struct page * page)
-{
-	if(page->pprev_hash) {
-		if(page->next_hash)
-			page->next_hash->pprev_hash = page->pprev_hash;
-		*page->pprev_hash = page->next_hash;
-		page->pprev_hash = NULL;
-	}
-	page_cache_size--;
-}
-
-static inline void __add_page_to_hash_queue(struct page * page, struct page **p)
-{
-	page_cache_size++;
-	if((page->next_hash = *p) != NULL)
-		(*p)->pprev_hash = &page->next_hash;
-	*p = page;
-	page->pprev_hash = p;
-}
+extern int add_to_page_cache_unique(struct page * page, struct inode * inode, unsigned long offset, struct page **hash);
 
 static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset)
 {
 	__add_page_to_hash_queue(page, page_hash(inode,offset));
 }
 
-static inline void remove_page_from_inode_queue(struct page * page)
-{
-	struct inode * inode = page->inode;
-
-	page->inode = NULL;
-	inode->i_nrpages--;
-	if (inode->i_pages == page)
-		inode->i_pages = page->next;
-	if (page->next)
-		page->next->prev = page->prev;
-	if (page->prev)
-		page->prev->next = page->next;
-	page->next = NULL;
-	page->prev = NULL;
-}
-
 static inline void add_page_to_inode_queue(struct inode * inode, struct page * page)
 {
 	struct page **p = &inode->i_pages;
@@ -142,11 +95,13 @@
 	*p = page;
 }
 
-extern void __wait_on_page(struct page *);
+extern void ___wait_on_page(struct page *);
+
 static inline void wait_on_page(struct page * page)
 {
+
 	if (PageLocked(page))
-		__wait_on_page(page);
+		___wait_on_page(page);
 }
 
 extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
diff -u --recursive --new-file v2.3.6/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.3.6/linux/include/linux/pci.h	Wed Jun  9 16:59:16 1999
+++ linux/include/linux/pci.h	Sun Jun 20 17:46:14 1999
@@ -280,7 +280,7 @@
 /*
  * Vendor and card ID's: sort these numerically according to vendor
  * (and according to card ID within vendor). Send all updates to
- * <linux-pcisupport@cck.uni-kl.de>.
+ * <pci-ids@ucw.cz>.
  */
 #define PCI_VENDOR_ID_COMPAQ		0x0e11
 #define PCI_DEVICE_ID_COMPAQ_1280	0x3033
@@ -1253,6 +1253,8 @@
 struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
 struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from);
 struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
+
+#define PCI_ANY_ID (~0)
 
 #define pci_present pcibios_present
 int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
diff -u --recursive --new-file v2.3.6/linux/include/linux/pipe_fs_i.h linux/include/linux/pipe_fs_i.h
--- v2.3.6/linux/include/linux/pipe_fs_i.h	Tue May 11 14:37:40 1999
+++ linux/include/linux/pipe_fs_i.h	Wed Jun 16 19:26:27 1999
@@ -12,15 +12,15 @@
 	unsigned int writers;
 };
 
-#define PIPE_WAIT(inode)	((inode).u.pipe_i.wait)
-#define PIPE_BASE(inode)	((inode).u.pipe_i.base)
-#define PIPE_START(inode)	((inode).u.pipe_i.start)
+#define PIPE_WAIT(inode)	((inode).i_pipe->wait)
+#define PIPE_BASE(inode)	((inode).i_pipe->base)
+#define PIPE_START(inode)	((inode).i_pipe->start)
 #define PIPE_LEN(inode)		((inode).i_size)
-#define PIPE_RD_OPENERS(inode)	((inode).u.pipe_i.rd_openers)
-#define PIPE_WR_OPENERS(inode)	((inode).u.pipe_i.wr_openers)
-#define PIPE_READERS(inode)	((inode).u.pipe_i.readers)
-#define PIPE_WRITERS(inode)	((inode).u.pipe_i.writers)
-#define PIPE_LOCK(inode)	((inode).u.pipe_i.lock)
+#define PIPE_RD_OPENERS(inode)	((inode).i_pipe->rd_openers)
+#define PIPE_WR_OPENERS(inode)	((inode).i_pipe->wr_openers)
+#define PIPE_READERS(inode)	((inode).i_pipe->readers)
+#define PIPE_WRITERS(inode)	((inode).i_pipe->writers)
+#define PIPE_LOCK(inode)	((inode).i_pipe->lock)
 #define PIPE_SIZE(inode)	PIPE_LEN(inode)
 
 #define PIPE_EMPTY(inode)	(PIPE_SIZE(inode)==0)
diff -u --recursive --new-file v2.3.6/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.3.6/linux/include/linux/proc_fs.h	Tue Jun  8 23:04:14 1999
+++ linux/include/linux/proc_fs.h	Sun Jun 20 17:46:43 1999
@@ -37,6 +37,7 @@
 	PROC_KSYMS,
 	PROC_DMA,	
 	PROC_IOPORTS,
+	PROC_MEMORY,
 	PROC_PROFILE, /* whether enabled or not */
 	PROC_CMDLINE,
 	PROC_SYS,
diff -u --recursive --new-file v2.3.6/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.3.6/linux/include/linux/sched.h	Tue Jun  8 23:03:44 1999
+++ linux/include/linux/sched.h	Sun Jun 20 17:46:18 1999
@@ -286,7 +286,7 @@
 	gid_t gid,egid,sgid,fsgid;
 	int ngroups;
 	gid_t	groups[NGROUPS];
-        kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
+	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
 	struct user_struct *user;
 /* limits */
 	struct rlimit rlim[RLIM_NLIMITS];
@@ -601,7 +601,7 @@
 #else
 	if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0)
 #endif
-        {
+	{
 		current->flags |= PF_SUPERPRIV;
 		return 1;
 	}
diff -u --recursive --new-file v2.3.6/linux/include/linux/smb.h linux/include/linux/smb.h
--- v2.3.6/linux/include/linux/smb.h	Sun Dec 27 22:18:28 1998
+++ linux/include/linux/smb.h	Wed Jun 16 19:26:27 1999
@@ -57,7 +57,7 @@
 	/* The following are NT LM 0.12 options */
 	__u32              maxraw;
 	__u32              capabilities;
-	__u16              serverzone;
+	__s16              serverzone;
 };
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v2.3.6/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h
--- v2.3.6/linux/include/linux/smb_fs.h	Tue Jun  8 23:05:13 1999
+++ linux/include/linux/smb_fs.h	Sun Jun 20 17:47:56 1999
@@ -77,6 +77,22 @@
 #define SMB_FIX_OLDATTR	0x0002	/* Use core getattr (Win 95 speedup) */
 #define SMB_FIX_DIRATTR	0x0004	/* Use find_first for getattr */
 
+
+/* NT1 protocol capability bits */
+#define SMB_CAP_RAW_MODE         0x0001
+#define SMB_CAP_MPX_MODE         0x0002
+#define SMB_CAP_UNICODE          0x0004
+#define SMB_CAP_LARGE_FILES      0x0008
+#define SMB_CAP_NT_SMBS          0x0010
+#define SMB_CAP_RPC_REMOTE_APIS  0x0020
+#define SMB_CAP_STATUS32         0x0040
+#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080
+#define SMB_CAP_LOCK_AND_READ    0x0100
+#define SMB_CAP_NT_FIND          0x0200
+#define SMB_CAP_DFS              0x1000
+#define SMB_CAP_LARGE_READX      0x4000
+
+
 /* linux/fs/smbfs/mmap.c */
 int smb_mmap(struct file *, struct vm_area_struct *);
 
diff -u --recursive --new-file v2.3.6/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.3.6/linux/include/linux/swap.h	Tue Jun  8 23:03:40 1999
+++ linux/include/linux/swap.h	Sun Jun 20 17:46:14 1999
@@ -67,7 +67,7 @@
 extern int nr_free_pages;
 extern atomic_t nr_async_pages;
 extern struct inode swapper_inode;
-extern unsigned long page_cache_size;
+extern atomic_t page_cache_size;
 extern int buffermem;
 
 /* Incomplete types for prototype declarations: */
@@ -107,6 +107,7 @@
 /*
  * Make these inline later once they are working properly.
  */
+extern void __delete_from_swap_cache(struct page *page);
 extern void delete_from_swap_cache(struct page *page);
 extern void free_page_and_swap_cache(unsigned long addr);
 
@@ -163,7 +164,7 @@
 	unsigned int count;
 	if (PageReserved(page))
 		return 1;
-	count = atomic_read(&page->count);
+	count = page_count(page);
 	if (PageSwapCache(page))
 		count += swap_count(page->offset) - 2;
 	if (PageFreeAfter(page))
diff -u --recursive --new-file v2.3.6/linux/include/linux/synclink.h linux/include/linux/synclink.h
--- v2.3.6/linux/include/linux/synclink.h	Fri Mar 12 08:38:44 1999
+++ linux/include/linux/synclink.h	Wed Jun 16 19:26:27 1999
@@ -1,6 +1,8 @@
 /*
  * SyncLink Multiprotocol Serial Adapter Driver
  *
+ * ==FILEDATE 19990523==
+ *
  * Copyright (C) 1998 by Microgate Corporation
  * 
  * Redistribution of this file is permitted under 
@@ -66,11 +68,16 @@
 #define HDLC_FLAG_AUTO_RTS		0x0080
 #define HDLC_FLAG_RXC_DPLL		0x0100
 #define HDLC_FLAG_RXC_BRG		0x0200
+#define HDLC_FLAG_RXC_TXCPIN	0x8000
+#define HDLC_FLAG_RXC_RXCPIN	0x0000
 #define HDLC_FLAG_TXC_DPLL		0x0400
 #define HDLC_FLAG_TXC_BRG		0x0800
+#define HDLC_FLAG_TXC_TXCPIN	0x0000
+#define HDLC_FLAG_TXC_RXCPIN	0x0008
 #define HDLC_FLAG_DPLL_DIV8		0x1000
 #define HDLC_FLAG_DPLL_DIV16		0x2000
 #define HDLC_FLAG_DPLL_DIV32		0x0000
+#define HDLC_FLAG_HDLC_LOOPMODE		0x4000
 
 #define HDLC_CRC_NONE			0
 #define HDLC_CRC_16_CCITT		1
@@ -87,6 +94,7 @@
 #define HDLC_ENCODING_NRZB			1
 #define HDLC_ENCODING_NRZI_MARK			2
 #define HDLC_ENCODING_NRZI_SPACE		3
+#define HDLC_ENCODING_NRZI			HDLC_ENCODING_NRZI_SPACE
 #define HDLC_ENCODING_BIPHASE_MARK		4
 #define HDLC_ENCODING_BIPHASE_SPACE		5
 #define HDLC_ENCODING_BIPHASE_LEVEL		6
@@ -227,17 +235,19 @@
  * MGSL_IOCTXABORT	abort transmitting frame (HDLC)
  * MGSL_IOCGSTATS	return current statistics
  * MGSL_IOCWAITEVENT	wait for specified event to occur
+ * MGSL_LOOPTXDONE	transmit in HDLC LoopMode done
  */
 #define MGSL_MAGIC_IOC	'm'
-#define MGSL_IOCSPARAMS		_IOW(MGSL_MAGIC_IOC,0,sizeof(MGSL_PARAMS))
-#define MGSL_IOCGPARAMS		_IOR(MGSL_MAGIC_IOC,1,sizeof(MGSL_PARAMS))
+#define MGSL_IOCSPARAMS		_IOW(MGSL_MAGIC_IOC,0,struct _MGSL_PARAMS)
+#define MGSL_IOCGPARAMS		_IOR(MGSL_MAGIC_IOC,1,struct _MGSL_PARAMS)
 #define MGSL_IOCSTXIDLE		_IO(MGSL_MAGIC_IOC,2)
 #define MGSL_IOCGTXIDLE		_IO(MGSL_MAGIC_IOC,3)
 #define MGSL_IOCTXENABLE	_IO(MGSL_MAGIC_IOC,4)
 #define MGSL_IOCRXENABLE	_IO(MGSL_MAGIC_IOC,5)
 #define MGSL_IOCTXABORT		_IO(MGSL_MAGIC_IOC,6)
 #define MGSL_IOCGSTATS		_IO(MGSL_MAGIC_IOC,7)
-#define MGSL_IOCWAITEVENT	_IO(MGSL_MAGIC_IOC,8)
+#define MGSL_IOCWAITEVENT	_IOWR(MGSL_MAGIC_IOC,8,int)
 #define MGSL_IOCCLRMODCOUNT	_IO(MGSL_MAGIC_IOC,15)
+#define MGSL_IOCLOOPTXDONE	_IO(MGSL_MAGIC_IOC,9)
 
 #endif /* _SYNCLINK_H_ */
diff -u --recursive --new-file v2.3.6/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.3.6/linux/include/linux/sysv_fs.h	Tue Jun  8 23:05:12 1999
+++ linux/include/linux/sysv_fs.h	Sun Jun 20 17:47:55 1999
@@ -387,6 +387,7 @@
 extern int sysv_bmap(struct inode *,int);
 
 extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int);
+extern int sysv_getblk_block(struct inode *, long, int, int *, int *);
 extern struct buffer_head * sysv_file_bread(struct inode *, int, int);
 extern ssize_t sysv_file_read(struct file *, char *, size_t, loff_t *);
 
diff -u --recursive --new-file v2.3.6/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.3.6/linux/include/linux/ufs_fs.h	Tue May 25 14:56:58 1999
+++ linux/include/linux/ufs_fs.h	Wed Jun 16 19:26:27 1999
@@ -537,6 +537,7 @@
 extern void ufs_write_inode (struct inode *);
 extern void ufs_delete_inode (struct inode *);
 extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *);
+extern int ufs_getfrag_block (struct inode *, long, int, int *, int *);
 extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
 
 /* namei.c */
diff -u --recursive --new-file v2.3.6/linux/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h
--- v2.3.6/linux/include/linux/umsdos_fs_i.h	Sat May 15 15:05:37 1999
+++ linux/include/linux/umsdos_fs_i.h	Wed Jun 16 19:26:27 1999
@@ -28,9 +28,8 @@
  * 
  * For directory, we also have a reference to the inode of its
  * own EMD file. Also, we have dir_locking_info to help synchronise
- * file creation and file lookup. This data is sharing space with
- * the pipe_inode_info not used by directory. See also msdos_fs_i.h
- * for more information about pipe_inode_info and msdos_inode_info.
+ * file creation and file lookup. See also msdos_fs_i.h for more 
+ * information about msdos_inode_info.
  * 
  * Special file and fifo do have an inode which correspond to an
  * empty MSDOS file.
@@ -38,11 +37,6 @@
  * symlink are processed mostly like regular file. The content is the
  * link.
  * 
- * fifos add there own extension to the inode. I have reserved some
- * space for fifos side by side with msdos_inode_info. This is just
- * to for the show, because msdos_inode_info already include the
- * pipe_inode_info.
- * 
  * The UMSDOS specific extension is placed after the union.
  */
 
@@ -60,7 +54,6 @@
 struct umsdos_inode_info {
 	union {
 		struct msdos_inode_info msdos_info;
-		struct pipe_inode_info pipe_info;
 		struct dir_locking_info dir_info;
 	} u;
 	int i_patched;			/* Inode has been patched */
diff -u --recursive --new-file v2.3.6/linux/init/main.c linux/init/main.c
--- v2.3.6/linux/init/main.c	Tue Jun  1 23:25:48 1999
+++ linux/init/main.c	Wed Jun 16 19:26:27 1999
@@ -1139,6 +1139,7 @@
  * Interrupts are still disabled. Do necessary setups, then
  * enable them
  */
+	lock_kernel();
 	printk(linux_banner);
 	setup_arch(&command_line, &memory_start, &memory_end);
 	memory_start = paging_init(memory_start,memory_end);
@@ -1205,6 +1206,7 @@
 	 */
 	smp_init();
 	kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	unlock_kernel();
 	current->need_resched = 1;
  	cpu_idle(NULL);
 }
diff -u --recursive --new-file v2.3.6/linux/ipc/shm.c linux/ipc/shm.c
--- v2.3.6/linux/ipc/shm.c	Mon Jun  7 12:20:50 1999
+++ linux/ipc/shm.c	Wed Jun 16 19:26:27 1999
@@ -675,7 +675,7 @@
 
 done:	/* pte_val(pte) == shp->shm_pages[idx] */
 	current->min_flt++;
-	atomic_inc(&mem_map[MAP_NR(pte_page(pte))].count);
+	get_page(mem_map + MAP_NR(pte_page(pte)));
 	return pte_page(pte);
 }
 
@@ -730,7 +730,7 @@
 		swap_free (swap_nr);
 		return 0;
 	}
-	if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) != 1)
+	if (page_count(mem_map + MAP_NR(pte_page(page))) != 1)
 		goto check_table;
 	shp->shm_pages[idx] = swap_nr;
 	rw_swap_page_nocache (WRITE, swap_nr, (char *) pte_page(page));
@@ -751,7 +751,7 @@
 
 	pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
 	shp->shm_pages[idx] = pte_val(pte);
-	atomic_inc(&mem_map[MAP_NR(page)].count);
+	get_page(mem_map + MAP_NR(page));
 	shm_rss++;
 
 	swap_free(entry);
diff -u --recursive --new-file v2.3.6/linux/kernel/acct.c linux/kernel/acct.c
--- v2.3.6/linux/kernel/acct.c	Tue Mar 23 16:57:38 1999
+++ linux/kernel/acct.c	Fri Jun 18 08:01:50 1999
@@ -333,10 +333,8 @@
 	fs = get_fs();
 	set_fs(KERNEL_DS);
 	inode = file->f_dentry->d_inode;
-	down(&inode->i_sem);
 	file->f_op->write(file, (char *)&ac,
 			       sizeof(struct acct), &file->f_pos);
-	up(&inode->i_sem);
 	set_fs(fs);
 	fput(file);
 	return 0;
diff -u --recursive --new-file v2.3.6/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.3.6/linux/kernel/ksyms.c	Tue Jun  8 10:47:58 1999
+++ linux/kernel/ksyms.c	Sat Jun 19 12:12:07 1999
@@ -106,7 +106,6 @@
 EXPORT_SYMBOL(remap_page_range);
 EXPORT_SYMBOL(max_mapnr);
 EXPORT_SYMBOL(high_memory);
-EXPORT_SYMBOL(update_vm_cache);
 EXPORT_SYMBOL(vmtruncate);
 EXPORT_SYMBOL(find_vma);
 EXPORT_SYMBOL(get_unmapped_area);
@@ -168,14 +167,12 @@
 EXPORT_SYMBOL(generic_file_read);
 EXPORT_SYMBOL(generic_file_write);
 EXPORT_SYMBOL(generic_file_mmap);
-EXPORT_SYMBOL(generic_readpage);
 EXPORT_SYMBOL(file_lock_table);
 EXPORT_SYMBOL(posix_lock_file);
 EXPORT_SYMBOL(posix_test_lock);
 EXPORT_SYMBOL(posix_block_lock);
 EXPORT_SYMBOL(posix_unblock_lock);
 EXPORT_SYMBOL(dput);
-EXPORT_SYMBOL(get_cached_page);
 EXPORT_SYMBOL(put_cached_page);
 EXPORT_SYMBOL(is_root_busy);
 EXPORT_SYMBOL(prune_dcache);
@@ -302,10 +299,12 @@
 EXPORT_SYMBOL(enable_hlt);
 #endif
 
-/* IO port handling */
-EXPORT_SYMBOL(check_region);
-EXPORT_SYMBOL(request_region);
-EXPORT_SYMBOL(release_region);
+/* resource handling */
+EXPORT_SYMBOL(check_resource);
+EXPORT_SYMBOL(request_resource);
+EXPORT_SYMBOL(release_resource);
+EXPORT_SYMBOL(occupy_resource);
+EXPORT_SYMBOL(vacate_resource);
 
 /* process management */
 EXPORT_SYMBOL(__wake_up);
@@ -361,7 +360,6 @@
 EXPORT_SYMBOL(__wait_on_super);
 EXPORT_SYMBOL(file_fsync);
 EXPORT_SYMBOL(clear_inode);
-EXPORT_SYMBOL(refile_buffer);
 EXPORT_SYMBOL(nr_async_pages);
 EXPORT_SYMBOL(___strtok);
 EXPORT_SYMBOL(init_special_inode);
diff -u --recursive --new-file v2.3.6/linux/kernel/resource.c linux/kernel/resource.c
--- v2.3.6/linux/kernel/resource.c	Thu Nov 12 09:58:32 1998
+++ linux/kernel/resource.c	Fri Jun 18 12:43:41 1999
@@ -1,10 +1,17 @@
 /*
  *	linux/kernel/resource.c
  *
- * Copyright (C) 1995	Linus Torvalds
- *			David Hinds
+ * Copyright (C) 1995, 1999	Linus Torvalds
+ *				David Hinds
  *
- * Kernel io-region resource management
+ * Kernel resource management
+ *
+ * We now distinguish between claiming space for devices (using the
+ * 'occupy' and 'vacate' calls), and associating a resource with a
+ * device driver (with the 'request', 'release', and 'check' calls).
+ * A resource can be claimed even if there is no associated driver
+ * (by occupying with name=NULL).  Vacating a resource makes it
+ * available for other dynamically configured devices.
  */
 
 #include <linux/sched.h>
@@ -12,47 +19,59 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 
-#define IOTABLE_SIZE 128
+#define RSRC_TABLE_SIZE 128
 
-typedef struct resource_entry_t {
+struct resource_entry {
 	u_long from, num;
 	const char *name;
-	struct resource_entry_t *next;
-} resource_entry_t;
+	struct resource_entry *next;
+};
 
-static resource_entry_t iolist = { 0, 0, "", NULL };
+struct resource_entry res_list[] = {
+    { 0, 0, NULL, NULL }, /* IO */
+    { 0, 0, NULL, NULL }  /* mem */
+};
 
-static resource_entry_t iotable[IOTABLE_SIZE];
+static struct resource_entry rsrc_table[RSRC_TABLE_SIZE];
 
 /*
- * This generates the report for /proc/ioports
+ * This generates reports for /proc/ioports and /proc/memory
  */
-int get_ioport_list(char *buf)
+int get_resource_list(int class, char *buf)
 {
-	resource_entry_t *p;
+	struct resource_entry *root = &res_list[class];
+	struct resource_entry *p;
 	int len = 0;
-
-	for (p = iolist.next; (p) && (len < 4000); p = p->next)
-		len += sprintf(buf+len, "%04lx-%04lx : %s\n",
-			   p->from, p->from+p->num-1, p->name);
+	char *fmt = (class == RES_IO) ?
+		"%04lx-%04lx : %s\n" : "%08lx-%08lx : %s\n";
+	
+	for (p = root->next; (p) && (len < 4000); p = p->next)
+		len += sprintf(buf+len, fmt, p->from, p->from+p->num-1,
+			       (p->name ? p->name : "occupied"));
 	if (p)
 		len += sprintf(buf+len, "4K limit reached!\n");
 	return len;
 }
 
 /*
- * The workhorse function: find where to put a new entry
+ * Basics: find a matching resource entry, or find an insertion point
  */
-static resource_entry_t *find_gap(resource_entry_t *root,
-				  u_long from, u_long num)
+static struct resource_entry *
+find_match(struct resource_entry *root, u_long from, u_long num)
 {
-	unsigned long flags;
-	resource_entry_t *p;
-	
+	struct resource_entry *p;
+	for (p = root; p; p = p->next)
+		if ((p->from == from) && (p->num == num))
+			return p;
+	return NULL;
+}
+
+static struct resource_entry *
+find_gap(struct resource_entry *root, u_long from, u_long num)
+{
+	struct resource_entry *p;
 	if (from > from+num-1)
 		return NULL;
-	save_flags(flags);
-	cli();
 	for (p = root; ; p = p->next) {
 		if ((p != root) && (p->from+p->num-1 >= from)) {
 			p = NULL;
@@ -61,123 +80,147 @@
 		if ((p->next == NULL) || (p->next->from > from+num-1))
 			break;
 	}
-	restore_flags(flags);
 	return p;
 }
 
 /*
- * Call this from the device driver to register the ioport region.
+ * Call this from a driver to assert ownership of a resource
  */
-void request_region(unsigned long from, unsigned long num, const char *name)
+void request_resource(int class, unsigned long from,
+		      unsigned long num, const char *name)
 {
-	resource_entry_t *p;
+	struct resource_entry *root = &res_list[class];
+	struct resource_entry *p;
+	long flags;
 	int i;
 
-	for (i = 0; i < IOTABLE_SIZE; i++)
-		if (iotable[i].num == 0)
+	p = find_match(root, from, num);
+	if (p) {
+		p->name = name;
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < RSRC_TABLE_SIZE; i++)
+		if (rsrc_table[i].num == 0)
 			break;
-	if (i == IOTABLE_SIZE)
-		printk("warning: ioport table is full\n");
+	if (i == RSRC_TABLE_SIZE)
+		printk("warning: resource table is full\n");
 	else {
-		p = find_gap(&iolist, from, num);
-		if (p == NULL)
+		p = find_gap(root, from, num);
+		if (p == NULL) {
+			restore_flags(flags);
 			return;
-		iotable[i].name = name;
-		iotable[i].from = from;
-		iotable[i].num = num;
-		iotable[i].next = p->next;
-		p->next = &iotable[i];
-		return;
+		}
+		rsrc_table[i].name = name;
+		rsrc_table[i].from = from;
+		rsrc_table[i].num = num;
+		rsrc_table[i].next = p->next;
+		p->next = &rsrc_table[i];
 	}
+	restore_flags(flags);
 }
 
 /* 
- * Call this when the device driver is unloaded
+ * Call these when a driver is unloaded but the device remains
  */
-void release_region(unsigned long from, unsigned long num)
+void release_resource(int class, unsigned long from, unsigned long num)
 {
-	resource_entry_t *p, *q;
-
-	for (p = &iolist; ; p = q) {
-		q = p->next;
-		if (q == NULL)
-			break;
-		if ((q->from == from) && (q->num == num)) {
-			q->num = 0;
-			p->next = q->next;
-			return;
-		}
-	}
+	struct resource_entry *root = &res_list[class];
+	struct resource_entry *p;
+	p = find_match(root, from, num);
+	if (p) p->name = NULL;
 }
 
 /*
- * Call this to check the ioport region before probing
+ * Call these to check a region for conflicts before probing
  */
-int check_region(unsigned long from, unsigned long num)
+int check_resource(int class, unsigned long from, unsigned long num)
 {
-	return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0;
+	struct resource_entry *root = &res_list[class];
+	struct resource_entry *p;
+	p = find_match(root, from, num);
+	if (p != NULL)
+		return (p->name != NULL) ? -EBUSY : 0;
+	return (find_gap(root, from, num) == NULL) ? -EBUSY : 0;
 }
 
-#ifdef __sparc__   /* Why to carry unused code on other architectures? */
 /*
- * This is for architectures with MMU-managed ports (sparc).
+ * Call this to claim a resource for a piece of hardware
  */
-unsigned long occupy_region(unsigned long base, unsigned long end,
-			    unsigned long num, unsigned int align, const char *name)
+unsigned long occupy_resource(int class, unsigned long base,
+			      unsigned long end, unsigned long num,
+			      unsigned long align, const char *name)
 {
+	struct resource_entry *root = &res_list[class];
 	unsigned long from = 0, till;
 	unsigned long flags;
 	int i;
-	resource_entry_t *p;		/* Scanning ptr */
-	resource_entry_t *p1;		/* === p->next */
-	resource_entry_t *s;		/* Found slot */
+	struct resource_entry *p, *q;
 
-	if (base > end-1)
-		return 0;
-	if (num > end - base)
+	if ((base > end-1) || (num > end - base))
 		return 0;
 
-	for (i = 0; i < IOTABLE_SIZE; i++)
-		if (iotable[i].num == 0)
+	for (i = 0; i < RSRC_TABLE_SIZE; i++)
+		if (rsrc_table[i].num == 0)
 			break;
-	if (i == IOTABLE_SIZE) {
-		/* Driver prints a warning typically. */
+	if (i == RSRC_TABLE_SIZE)
 		return 0;
-	}
 
 	save_flags(flags);
 	cli();
 	/* printk("occupy: search in %08lx[%08lx] ", base, end - base); */
-	s = NULL;
-	for (p = &iolist; p != NULL; p = p1) {
-		p1 = p->next;
+	for (p = root; p != NULL; p = q) {
+		q = p->next;
 		/* Find window in list */
-		from = (p->from+p->num + align-1) & ~((unsigned long)align-1);
-		till = (p1 == NULL)? (unsigned long) (0 - (unsigned long)align): p1->from;
+		from = (p->from+p->num + align-1) & ~(align-1);
+		till = (q == NULL) ? (0 - align) : q->from;
 		/* printk(" %08lx:%08lx", from, till); */
 		/* Clip window with base and end */
 		if (from < base) from = base;
 		if (till > end) till = end;
 		/* See if result is large enougth */
-		if (from < till && from + num < till) {
-			s = p;
+		if ((from < till) && (from + num < till))
 			break;
-		}
 	}
 	/* printk("\r\n"); */
 	restore_flags(flags);
 
-	if (s == NULL)
+	if (p == NULL)
 		return 0;
 
-	iotable[i].name = name;
-	iotable[i].from = from;
-	iotable[i].num = num;
-	iotable[i].next = s->next;
-	s->next = &iotable[i];
+	rsrc_table[i].name = name;
+	rsrc_table[i].from = from;
+	rsrc_table[i].num = num;
+	rsrc_table[i].next = p->next;
+	p->next = &rsrc_table[i];
 	return from;
 }
-#endif
+
+/*
+ * Call this when a resource becomes available for other hardware
+ */
+void vacate_resource(int class, unsigned long from, unsigned long num)
+{
+	struct resource_entry *root = &res_list[class];
+	struct resource_entry *p, *q;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	for (p = root; ; p = q) {
+		q = p->next;
+		if (q == NULL)
+			break;
+		if ((q->from == from) && (q->num == num)) {
+			q->num = 0;
+			p->next = q->next;
+			break;
+		}
+	}
+	restore_flags(flags);
+}
 
 /* Called from init/main.c to reserve IO ports. */
 void __init reserve_setup(char *str, int *ints)
diff -u --recursive --new-file v2.3.6/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.3.6/linux/kernel/sysctl.c	Sat Feb  6 12:22:24 1999
+++ linux/kernel/sysctl.c	Sat Jun 19 11:45:29 1999
@@ -121,11 +121,14 @@
 	NULL,		/* rename */
 	NULL,		/* readlink */
 	NULL,		/* follow_link */
+	NULL,		/* bmap */
 	NULL,		/* readpage */
 	NULL,		/* writepage */
-	NULL,		/* bmap */
+	NULL,		/* flushpage */
 	NULL,		/* truncate */
-	proc_sys_permission
+	proc_sys_permission, /* permission */
+	NULL,		/* smap */
+	NULL		/* revalidate */
 };
 
 extern struct proc_dir_entry proc_sys_root;
diff -u --recursive --new-file v2.3.6/linux/mm/filemap.c linux/mm/filemap.c
--- v2.3.6/linux/mm/filemap.c	Wed Jun  2 13:47:49 1999
+++ linux/mm/filemap.c	Sun Jun 20 16:21:41 1999
@@ -1,7 +1,7 @@
 /*
  *	linux/mm/filemap.c
  *
- * Copyright (C) 1994, 1995  Linus Torvalds
+ * Copyright (C) 1994-1999  Linus Torvalds
  */
 
 /*
@@ -29,9 +29,12 @@
  * though.
  *
  * Shared mappings now work. 15.8.1995  Bruno.
+ *
+ * finished 'unifying' the page and buffer cache and SMP-threaded the
+ * page-cache, 21.05.1999, Ingo Molnar <mingo@redhat.com>
  */
 
-unsigned long page_cache_size = 0;
+atomic_t page_cache_size = ATOMIC_INIT(0);
 struct page * page_hash_table[PAGE_HASH_SIZE];
 
 /* 
@@ -50,38 +53,97 @@
 static kmem_cache_t *pio_request_cache;
 static DECLARE_WAIT_QUEUE_HEAD(pio_wait);
 
+spinlock_t pagecache_lock = SPIN_LOCK_UNLOCKED;
+
+
 static inline void 
 make_pio_request(struct file *, unsigned long, unsigned long);
 
+void __add_page_to_hash_queue(struct page * page, struct page **p){
+	atomic_inc(&page_cache_size);
+	if((page->next_hash = *p) != NULL)
+		(*p)->pprev_hash = &page->next_hash;
+	*p = page;
+	page->pprev_hash = p;
+	if (page->buffers)
+		PAGE_BUG(page);
+}
+
+static void remove_page_from_hash_queue(struct page * page)
+{
+	if(page->pprev_hash) {
+		if(page->next_hash)
+			page->next_hash->pprev_hash = page->pprev_hash;
+		*page->pprev_hash = page->next_hash;
+		page->pprev_hash = NULL;
+	}
+	atomic_dec(&page_cache_size);
+}
+
+static void remove_page_from_inode_queue(struct page * page)
+{
+	struct inode * inode = page->inode;
+	struct page *prev, *next;
+
+	inode->i_nrpages--;
+	next = page->next;
+	prev = page->prev;
+	if (inode->i_pages == page)
+		inode->i_pages = next;
+	if (next)
+		next->prev = prev;
+	if (prev)
+		prev->next = next;
+	page->next = NULL;
+	page->prev = NULL;
+}
 
 /*
- * Invalidate the pages of an inode, removing all pages that aren't
- * locked down (those are sure to be up-to-date anyway, so we shouldn't
- * invalidate them).
+ * Remove a page from the page cache and free it. Caller has to make
+ * sure the page is locked and that nobody else uses it - or that usage
+ * is safe.
  */
+void remove_inode_page(struct page *page)
+{
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	spin_lock(&pagecache_lock);
+	remove_page_from_inode_queue(page);
+	remove_page_from_hash_queue(page);
+	page->inode = NULL;
+	spin_unlock(&pagecache_lock);
+}
+
 void invalidate_inode_pages(struct inode * inode)
 {
 	struct page ** p;
 	struct page * page;
 
+repeat:
+	spin_lock(&pagecache_lock);
 	p = &inode->i_pages;
 	while ((page = *p) != NULL) {
-		if (PageLocked(page)) {
-			p = &page->next;
-			continue;
+		get_page(page);
+		if (TryLockPage(page)) {
+			spin_unlock(&pagecache_lock);
+			wait_on_page(page);
+			page_cache_release(page);
+			goto repeat;
 		}
-		inode->i_nrpages--;
-		if ((*p = page->next) != NULL)
-			(*p)->prev = page->prev;
-		page->next = NULL;
-		page->prev = NULL;
+		if (page_count(page) != 2)
+			printk("hm, busy page invalidated? (not necesserily a bug)\n");
+
+		remove_page_from_inode_queue(page);
 		remove_page_from_hash_queue(page);
 		page->inode = NULL;
+		UnlockPage(page);
+		page_cache_release(page);
 		page_cache_release(page);
-		continue;
+
 	}
+	spin_unlock(&pagecache_lock);
 }
-
 /*
  * Truncate the page cache at a set offset, removing the pages
  * that are beyond that offset (and zeroing out partial pages).
@@ -90,55 +152,90 @@
 {
 	struct page ** p;
 	struct page * page;
+	int partial = 0;
 
 repeat:
+	spin_lock(&pagecache_lock);
 	p = &inode->i_pages;
 	while ((page = *p) != NULL) {
 		unsigned long offset = page->offset;
 
 		/* page wholly truncated - free it */
 		if (offset >= start) {
-			if (PageLocked(page)) {
-				wait_on_page(page);
-				goto repeat;
-			}
-			inode->i_nrpages--;
-			if ((*p = page->next) != NULL)
-				(*p)->prev = page->prev;
-			page->next = NULL;
-			page->prev = NULL;
-			remove_page_from_hash_queue(page);
-			page->inode = NULL;
+			get_page(page);
+			spin_unlock(&pagecache_lock);
+
+			lock_page(page);
+
+			if (inode->i_op->flushpage)
+				inode->i_op->flushpage(inode, page, 0);
+
+			/*
+			 * We remove the page from the page cache
+			 * _after_ we have destroyed all buffer-cache
+			 * references to it. Otherwise some other process
+			 * might think this inode page is not in the
+			 * page cache and creates a buffer-cache alias
+			 * to it causing all sorts of fun problems ...
+			 */
+			remove_inode_page(page);
+
+			UnlockPage(page);
 			page_cache_release(page);
-			continue;
+			page_cache_release(page);
+
+			/*
+			 * We have done things without the pagecache lock,
+			 * so we'll have to repeat the scan.
+			 * It's not possible to deadlock here because
+			 * we are guaranteed to make progress. (ie. we have
+			 * just removed a page)
+			 */
+			goto repeat;
 		}
 		p = &page->next;
+		/*
+		 * there is only one partial page possible.
+		 */
+		if (partial)
+			continue;
+
 		offset = start - offset;
 		/* partial truncate, clear end of page */
 		if (offset < PAGE_CACHE_SIZE) {
-			unsigned long address = page_address(page);
+			unsigned long address;
+			get_page(page);
+			spin_unlock(&pagecache_lock);
+
+			lock_page(page);
+			partial = 1;
+
+			address = page_address(page);
 			memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset);
 			flush_page_to_ram(address);
+
+			if (inode->i_op->flushpage)
+				inode->i_op->flushpage(inode, page, offset);
+			/*
+			 * we have dropped the spinlock so we have to
+			 * restart.
+			 */
+			UnlockPage(page);
+			page_cache_release(page);
+			goto repeat;
 		}
 	}
+	spin_unlock(&pagecache_lock);
 }
 
-/*
- * Remove a page from the page cache and free it.
- */
-void remove_inode_page(struct page *page)
-{
-	remove_page_from_hash_queue(page);
-	remove_page_from_inode_queue(page);
-	page_cache_release(page);
-}
+extern atomic_t too_many_dirty_buffers;
 
 int shrink_mmap(int priority, int gfp_mask)
 {
 	static unsigned long clock = 0;
 	unsigned long limit = num_physpages;
 	struct page * page;
-	int count;
+	int count, users;
 
 	count = limit >> priority;
 
@@ -164,15 +261,67 @@
 		
 		referenced = test_and_clear_bit(PG_referenced, &page->flags);
 
-		if (PageLocked(page))
+		if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
 			continue;
 
-		if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
+		/*
+		 * Some common cases that we just short-circuit without
+		 * getting the locks - we need to re-check this once we
+		 * have the lock, but that's fine.
+		 */
+		users = page_count(page);
+		if (!users)
 			continue;
+		if (!page->buffers) {
+			if (!page->inode)
+				continue;
+			if (users > 1)
+				continue;
+		}
 
-		/* We can't free pages unless there's just one user */
-		if (atomic_read(&page->count) != 1)
+		/*
+		 * ok, now the page looks interesting. Re-check things
+		 * and keep the lock.
+		 */
+		spin_lock(&pagecache_lock);
+		if (!page->inode && !page->buffers) {
+			spin_unlock(&pagecache_lock);
 			continue;
+		}
+		if (!page_count(page)) {
+//			BUG();
+			spin_unlock(&pagecache_lock);
+			continue;
+		}
+		get_page(page);
+		if (TryLockPage(page)) {
+			spin_unlock(&pagecache_lock);
+			goto put_continue;
+		}
+
+		/*
+		 * we keep pagecache_lock locked and unlock it in
+		 * each branch, so that the page->inode case doesnt
+		 * have to re-grab it. Here comes the 'real' logic
+		 * to free memory:
+		 */
+
+		/* Is it a buffer page? */
+		if (page->buffers) {
+			kdev_t dev = page->buffers->b_dev;
+			spin_unlock(&pagecache_lock);
+			if (try_to_free_buffers(page))
+				goto made_progress;
+			if (!atomic_read(&too_many_dirty_buffers)) {
+				atomic_set(&too_many_dirty_buffers, 1);
+				balance_dirty(dev);
+			}
+			goto unlock_continue;
+		}
+
+		/* We can't free pages unless there's just one user */
+		if (page_count(page) != 2)
+			goto spin_unlock_continue;
 
 		count--;
 
@@ -182,77 +331,180 @@
 		 * were to be marked referenced..
 		 */
 		if (PageSwapCache(page)) {
-			if (referenced && swap_count(page->offset) != 1)
-				continue;
-			delete_from_swap_cache(page);
-			return 1;
+			spin_unlock(&pagecache_lock);
+			if (referenced && swap_count(page->offset) != 2)
+				goto unlock_continue;
+			__delete_from_swap_cache(page);
+			page_cache_release(page);
+			goto made_progress;
 		}	
 
-		if (referenced)
-			continue;
-
-		/* Is it a buffer page? */
-		if (page->buffers) {
-			if (buffer_under_min())
-				continue;
-			if (!try_to_free_buffers(page))
-				continue;
-			return 1;
-		}
-
 		/* is it a page-cache page? */
-		if (page->inode) {
-			if (pgcache_under_min())
-				continue;
-			remove_inode_page(page);
-			return 1;
-		}
+		if (!referenced && page->inode && !pgcache_under_min()) {
+			remove_page_from_inode_queue(page);
+			remove_page_from_hash_queue(page);
+			page->inode = NULL;
+			spin_unlock(&pagecache_lock);
 
+			page_cache_release(page);
+			goto made_progress;
+		}
+spin_unlock_continue:
+		spin_unlock(&pagecache_lock);
+unlock_continue:
+		UnlockPage(page);
+put_continue:
+		put_page(page);
 	} while (count > 0);
 	return 0;
+made_progress:
+	UnlockPage(page);
+	put_page(page);
+	return 1;
+}
+
+static inline struct page * __find_page_nolock(struct inode * inode, unsigned long offset, struct page *page)
+{
+	goto inside;
+
+	for (;;) {
+		page = page->next_hash;
+inside:
+		if (!page)
+			goto not_found;
+		if (page->inode != inode)
+			continue;
+		if (page->offset == offset)
+			break;
+	}
+not_found:
+	return page;
 }
 
 /*
- * Update a page cache copy, when we're doing a "write()" system call
- * See also "update_vm_cache()".
+ * By the time this is called, the page is locked and
+ * we don't have to worry about any races any more.
+ *
+ * Start the IO..
  */
-void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count)
+static int writeout_one_page(struct page *page)
 {
-	unsigned long offset, len;
+	struct buffer_head *bh, *head = page->buffers;
 
-	offset = (pos & ~PAGE_CACHE_MASK);
-	pos = pos & PAGE_CACHE_MASK;
-	len = PAGE_CACHE_SIZE - offset;
+	bh = head;
 	do {
-		struct page * page;
+		if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
+			continue;
 
-		if (len > count)
-			len = count;
-		page = find_page(inode, pos);
-		if (page) {
-			wait_on_page(page);
-			memcpy((void *) (offset + page_address(page)), buf, len);
-			page_cache_release(page);
-		}
-		count -= len;
-		buf += len;
-		len = PAGE_CACHE_SIZE;
-		offset = 0;
-		pos += PAGE_CACHE_SIZE;
-	} while (count);
+		bh->b_flushtime = 0;
+		ll_rw_block(WRITE, 1, &bh);	
+	} while ((bh = bh->b_this_page) != head);
+	return 0;
+}
+
+static int waitfor_one_page(struct page *page)
+{
+	int error = 0;
+	struct buffer_head *bh, *head = page->buffers;
+
+	bh = head;
+	do {
+		wait_on_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+			error = -EIO;
+	} while ((bh = bh->b_this_page) != head);
+	return error;
+}
+
+static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end, int (*fn)(struct page *))
+{
+	struct page *next;
+	int retval = 0;
+
+	start &= PAGE_MASK;
+
+	spin_lock(&pagecache_lock);
+	next = inode->i_pages;
+	while (next) {
+		struct page *page = next;
+		next = page->next;
+		if (!page->buffers)
+			continue;
+		if (page->offset >= end)
+			continue;
+		if (page->offset < start)
+			continue;
+
+		get_page(page);
+		spin_unlock(&pagecache_lock);
+		lock_page(page);
+
+		/* The buffers could have been free'd while we waited for the page lock */
+		if (page->buffers)
+			retval |= fn(page);
+
+		UnlockPage(page);
+		spin_lock(&pagecache_lock);
+		next = page->next;
+		page_cache_release(page);
+	}
+	spin_unlock(&pagecache_lock);
+
+	return retval;
+}
+
+/*
+ * Two-stage data sync: first start the IO, then go back and
+ * collect the information..
+ */
+int generic_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end)
+{
+	int retval;
+
+	retval = do_buffer_fdatasync(inode, start, end, writeout_one_page);
+	retval |= do_buffer_fdatasync(inode, start, end, waitfor_one_page);
+	return retval;
 }
 
-static inline void add_to_page_cache(struct page * page,
+/*
+ * This adds a page to the page cache, starting out as locked,
+ * owned by us, referenced, but not uptodate and with no errors.
+ */
+static inline void __add_to_page_cache(struct page * page,
 	struct inode * inode, unsigned long offset,
 	struct page **hash)
 {
-	atomic_inc(&page->count);
-	page->flags = (page->flags & ~((1 << PG_uptodate) | (1 << PG_error))) | (1 << PG_referenced);
+	unsigned long flags;
+
+	flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error));
+	page->flags = flags |  ((1 << PG_locked) | (1 << PG_referenced));
+	page->owner = (int)current;	/* REMOVEME */
+	get_page(page);
 	page->offset = offset;
 	add_page_to_inode_queue(inode, page);
 	__add_page_to_hash_queue(page, hash);
 }
 
+int add_to_page_cache_unique(struct page * page,
+	struct inode * inode, unsigned long offset,
+	struct page **hash)
+{
+	int err;
+	struct page *alias;
+
+	spin_lock(&pagecache_lock);
+	alias = __find_page_nolock(inode, offset, *hash);
+
+	err = 1;
+	if (!alias) {
+		__add_to_page_cache(page,inode,offset,hash);
+		err = 0;
+	}
+
+	spin_unlock(&pagecache_lock);
+	return err;
+}
+
 /*
  * Try to read ahead in the file. "page_cache" is a potentially free page
  * that we could use for the cache (if it is 0 we can try to create one,
@@ -275,45 +527,173 @@
 		if (offset >= inode->i_size)
 			break;
 		hash = page_hash(inode, offset);
-		page = __find_page(inode, offset, *hash);
-		if (!page) {
+		page = page_cache_entry(page_cache);
+		if (!add_to_page_cache_unique(page, inode, offset, hash)) {
 			/*
-			 * Ok, add the new page to the hash-queues...
+			 * We do not have to check the return value here
+			 * because it's a readahead.
 			 */
-			page = page_cache_entry(page_cache);
-			add_to_page_cache(page, inode, offset, hash);
 			inode->i_op->readpage(file, page);
 			page_cache = 0;
+			page_cache_release(page);
 		}
-		page_cache_release(page);
 	}
 	return page_cache;
 }
 
 /* 
- * Wait for IO to complete on a locked page.
+ * Wait for a page to get unlocked.
  *
  * This must be called with the caller "holding" the page,
  * ie with increased "page->count" so that the page won't
  * go away during the wait..
  */
-void __wait_on_page(struct page *page)
+void ___wait_on_page(struct page *page)
 {
 	struct task_struct *tsk = current;
 	DECLARE_WAITQUEUE(wait, tsk);
 
 	add_wait_queue(&page->wait, &wait);
-repeat:
-	tsk->state = TASK_UNINTERRUPTIBLE;
-	run_task_queue(&tq_disk);
-	if (PageLocked(page)) {
+	do {
+		tsk->state = TASK_UNINTERRUPTIBLE;
+		run_task_queue(&tq_disk);
+		if (!PageLocked(page))
+			break;
 		schedule();
-		goto repeat;
-	}
+	} while (PageLocked(page));
 	tsk->state = TASK_RUNNING;
 	remove_wait_queue(&page->wait, &wait);
 }
 
+/*
+ * Get an exclusive lock on the page..
+ */
+void lock_page(struct page *page)
+{
+	if (TryLockPage(page)) {
+		struct task_struct *tsk = current;
+		DECLARE_WAITQUEUE(wait, current);
+
+		run_task_queue(&tq_disk);
+		add_wait_queue(&page->wait, &wait);
+		tsk->state = TASK_UNINTERRUPTIBLE;
+
+		while (TryLockPage(page)) {
+			run_task_queue(&tq_disk);
+			schedule();
+			tsk->state = TASK_UNINTERRUPTIBLE;
+		}
+
+		remove_wait_queue(&page->wait, &wait);
+		tsk->state = TASK_RUNNING;
+	}
+}
+
+
+/*
+ * a rather lightweight function, finding and getting a reference to a
+ * hashed page atomically, waiting for it if it's locked.
+ */
+struct page * __find_get_page (struct inode * inode,
+				unsigned long offset, struct page **hash)
+{
+	struct page *page;
+
+	/*
+	 * We scan the hash list read-only. Addition to and removal from
+	 * the hash-list needs a held write-lock.
+	 */
+repeat:
+	spin_lock(&pagecache_lock);
+	page = __find_page_nolock(inode, offset, *hash);
+	if (page)
+		get_page(page);
+	spin_unlock(&pagecache_lock);
+
+	/* Found the page, sleep if locked. */
+	if (page && PageLocked(page)) {
+		struct task_struct *tsk = current;
+		DECLARE_WAITQUEUE(wait, tsk);
+
+		add_wait_queue(&page->wait, &wait);
+		tsk->state = TASK_UNINTERRUPTIBLE;
+
+		run_task_queue(&tq_disk);
+		if (PageLocked(page))
+			schedule();
+		tsk->state = TASK_RUNNING;
+		remove_wait_queue(&page->wait, &wait);
+
+		/*
+		 * The page might have been unhashed meanwhile. It's
+		 * not freed though because we hold a reference to it.
+		 * If this is the case then it will be freed _here_,
+		 * and we recheck the hash anyway.
+		 */
+		page_cache_release(page);
+		goto repeat;
+	}
+	/*
+	 * It's not locked so we can return the page and we hold
+	 * a reference to it.
+	 */
+	return page;
+}
+
+/*
+ * Get the lock to a page atomically.
+ */
+struct page * __find_lock_page (struct inode * inode,
+				unsigned long offset, struct page **hash)
+{
+	int locked;
+	struct page *page;
+
+	/*
+	 * We scan the hash list read-only. Addition to and removal from
+	 * the hash-list needs a held write-lock.
+	 */
+repeat:
+	spin_lock(&pagecache_lock);
+	page = __find_page_nolock(inode, offset, *hash);
+	locked = 0;
+	if (page) {
+		get_page(page);
+		if (TryLockPage(page))
+			locked = 1;
+	}
+	spin_unlock(&pagecache_lock);
+
+	/* Found the page, sleep if locked. */
+	if (page && locked) {
+		struct task_struct *tsk = current;
+		DECLARE_WAITQUEUE(wait, tsk);
+
+		add_wait_queue(&page->wait, &wait);
+		tsk->state = TASK_UNINTERRUPTIBLE;
+
+		run_task_queue(&tq_disk);
+		if (PageLocked(page))
+			schedule();
+		tsk->state = TASK_RUNNING;
+		remove_wait_queue(&page->wait, &wait);
+
+		/*
+		 * The page might have been unhashed meanwhile. It's
+		 * not freed though because we hold a reference to it.
+		 * If this is the case then it will be freed _here_,
+		 * and we recheck the hash anyway.
+		 */
+		page_cache_release(page);
+		goto repeat;
+	}
+	/*
+	 * It's not locked so we can return the page and we hold
+	 * a reference to it.
+	 */
+	return page;
+}
+
 #if 0
 #define PROFILE_READAHEAD
 #define DEBUG_READAHEAD
@@ -386,14 +766,14 @@
  * -------------------
  * The read ahead context fields of the "struct file" are the following:
  * - f_raend : position of the first byte after the last page we tried to
- *             read ahead.
+ *	       read ahead.
  * - f_ramax : current read-ahead maximum size.
  * - f_ralen : length of the current IO read block we tried to read-ahead.
  * - f_rawin : length of the current read-ahead window.
- *             if last read-ahead was synchronous then
- *                  f_rawin = f_ralen
- *             otherwise (was asynchronous)
- *                  f_rawin = previous value of f_ralen + f_ralen
+ *		if last read-ahead was synchronous then
+ *			f_rawin = f_ralen
+ *		otherwise (was asynchronous)
+ *			f_rawin = previous value of f_ralen + f_ralen
  *
  * Read-ahead limits:
  * ------------------
@@ -485,7 +865,7 @@
  * We will later force unplug device in order to force asynchronous read IO.
  */
 	else if (reada_ok && filp->f_ramax && raend >= PAGE_CACHE_SIZE &&
-	         ppos <= raend && ppos + filp->f_ralen >= raend) {
+		 ppos <= raend && ppos + filp->f_ralen >= raend) {
 /*
  * Add ONE page to max_ahead in order to try to have about the same IO max size
  * as synchronous read-ahead (MAX_READAHEAD + 1)*PAGE_CACHE_SIZE.
@@ -578,6 +958,7 @@
 	struct inode *inode = dentry->d_inode;
 	size_t pos, pgpos, page_cache;
 	int reada_ok;
+	int error;
 	int max_readahead = get_max_readahead(inode);
 
 	page_cache = 0;
@@ -633,33 +1014,22 @@
 		 * Try to find the data in the page cache..
 		 */
 		hash = page_hash(inode, pos & PAGE_CACHE_MASK);
-		page = __find_page(inode, pos & PAGE_CACHE_MASK, *hash);
+
+		spin_lock(&pagecache_lock);
+		page = __find_page_nolock(inode, pos & PAGE_CACHE_MASK, *hash);
 		if (!page)
 			goto no_cached_page;
-
 found_page:
-/*
- * Try to read ahead only if the current page is filled or being filled.
- * Otherwise, if we were reading ahead, decrease max read ahead size to
- * the minimum value.
- * In this context, that seems to may happen only on some read error or if 
- * the page has been rewritten.
- */
-		if (PageUptodate(page) || PageLocked(page))
-			page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache);
-		else if (reada_ok && filp->f_ramax > MIN_READAHEAD)
-				filp->f_ramax = MIN_READAHEAD;
+		get_page(page);
+		spin_unlock(&pagecache_lock);
 
-		wait_on_page(page);
-
-		if (!PageUptodate(page))
-			goto page_read_error;
-
-success:
-		/*
-		 * Ok, we have the page, it's up-to-date and ok,
-		 * so now we can finally copy it to user space...
-		 */
+		if (!Page_Uptodate(page))
+			goto page_not_up_to_date;
+page_ok:
+	/*
+	 * Ok, we have the page, and it's up-to-date, so
+	 * now we can copy it to user space...
+	 */
 	{
 		unsigned long offset, nr;
 
@@ -683,75 +1053,77 @@
 		break;
 	}
 
+/*
+ * Ok, the page was not immediately readable, so let's try to read ahead while we're at it..
+ */
+page_not_up_to_date:
+		page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache);
+
+		if (Page_Uptodate(page))
+			goto page_ok;
+
+		/* Get exclusive access to the page ... */
+		lock_page(page);
+		if (Page_Uptodate(page)) {
+			UnlockPage(page);
+			goto page_ok;
+		}
+
+readpage:
+		/* ... and start the actual read. The read will unlock the page. */
+		error = inode->i_op->readpage(filp, page);
+
+		if (!error) {
+			if (Page_Uptodate(page))
+				goto page_ok;
+
+			/* Again, try some read-ahead while waiting for the page to finish.. */
+			page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache);
+			wait_on_page(page);
+			if (Page_Uptodate(page))
+				goto page_ok;
+			error = -EIO;
+		}
+
+		/* UHHUH! A synchronous read error occurred. Report it */
+		desc->error = error;
+		page_cache_release(page);
+		break;
+
 no_cached_page:
 		/*
 		 * Ok, it wasn't cached, so we need to create a new
 		 * page..
+		 *
+		 * We get here with the page cache lock held.
 		 */
 		if (!page_cache) {
+			spin_unlock(&pagecache_lock);
 			page_cache = page_cache_alloc();
+			if (!page_cache) {
+				desc->error = -ENOMEM;
+				break;
+			}
+
 			/*
-			 * That could have slept, so go around to the
-			 * very beginning..
+			 * Somebody may have added the page while we
+			 * dropped the page cache lock. Check for that.
 			 */
-			if (page_cache)
-				continue;
-			desc->error = -ENOMEM;
-			break;
+			spin_lock(&pagecache_lock);
+			page = __find_page_nolock(inode, pos & PAGE_CACHE_MASK, *hash);
+			if (page)
+				goto found_page;
 		}
 
 		/*
 		 * Ok, add the new page to the hash-queues...
 		 */
 		page = page_cache_entry(page_cache);
-		page_cache = 0;
-		add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash);
-
-		/*
-		 * Error handling is tricky. If we get a read error,
-		 * the cached page stays in the cache (but uptodate=0),
-		 * and the next process that accesses it will try to
-		 * re-read it. This is needed for NFS etc, where the
-		 * identity of the reader can decide if we can read the
-		 * page or not..
-		 */
-/*
- * We have to read the page.
- * If we were reading ahead, we had previously tried to read this page,
- * That means that the page has probably been removed from the cache before 
- * the application process needs it, or has been rewritten.
- * Decrease max readahead size to the minimum value in that situation.
- */
-		if (reada_ok && filp->f_ramax > MIN_READAHEAD)
-			filp->f_ramax = MIN_READAHEAD;
-
-		{
-			int error = inode->i_op->readpage(filp, page);
-			if (!error)
-				goto found_page;
-			desc->error = error;
-			page_cache_release(page);
-			break;
-		}
+		__add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash);
+		spin_unlock(&pagecache_lock);
 
-page_read_error:
-		/*
-		 * We found the page, but it wasn't up-to-date.
-		 * Try to re-read it _once_. We do this synchronously,
-		 * because this happens only if there were errors.
-		 */
-		{
-			int error = inode->i_op->readpage(filp, page);
-			if (!error) {
-				wait_on_page(page);
-				if (PageUptodate(page) && !PageError(page))
-					goto success;
-				error = -EIO; /* Some unspecified error occurred.. */
-			}
-			desc->error = error;
-			page_cache_release(page);
-			break;
-		}
+		page_cache = 0;
+		goto readpage;
 	}
 
 	*ppos = pos;
@@ -787,6 +1159,7 @@
 {
 	ssize_t retval;
 
+	unlock_kernel();
 	retval = -EFAULT;
 	if (access_ok(VERIFY_WRITE, buf, count)) {
 		retval = 0;
@@ -804,6 +1177,7 @@
 				retval = desc.error;
 		}
 	}
+	lock_kernel();
 	return retval;
 }
 
@@ -812,17 +1186,14 @@
 	ssize_t written;
 	unsigned long count = desc->count;
 	struct file *file = (struct file *) desc->buf;
-	struct inode *inode = file->f_dentry->d_inode;
 	mm_segment_t old_fs;
 
 	if (size > count)
 		size = count;
-	down(&inode->i_sem);
 	old_fs = get_fs();
 	set_fs(KERNEL_DS);
 	written = file->f_op->write(file, area, size, &file->f_pos);
 	set_fs(old_fs);
-	up(&inode->i_sem);
 	if (written < 0) {
 		desc->error = written;
 		written = 0;
@@ -878,6 +1249,7 @@
 	if (retval)
 		goto fput_out;
 
+	unlock_kernel();
 	retval = 0;
 	if (count) {
 		read_descriptor_t desc;
@@ -887,7 +1259,7 @@
 		ppos = &in_file->f_pos;
 		if (offset) {
 			if (get_user(pos, offset))
-				goto fput_out;
+				goto fput_out_lock;
 			ppos = &pos;
 		}
 
@@ -904,7 +1276,8 @@
 			put_user(pos, offset);
 	}
 
-
+fput_out_lock:
+	lock_kernel();
 fput_out:
 	fput(out_file);
 fput_in:
@@ -934,17 +1307,21 @@
 	unsigned long offset, reada, i;
 	struct page * page, **hash;
 	unsigned long old_page, new_page;
+	int error;
 
 	new_page = 0;
 	offset = (address & PAGE_MASK) - area->vm_start + area->vm_offset;
 	if (offset >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm)
-		goto no_page;
+		goto no_page_nolock;
+
+	unlock_kernel();
 
 	/*
 	 * Do we have something in the page cache already?
 	 */
 	hash = page_hash(inode, offset);
-	page = __find_page(inode, offset, *hash);
+retry_find:
+	page = __find_get_page(inode, offset, hash);
 	if (!page)
 		goto no_cached_page;
 
@@ -960,15 +1337,17 @@
 			goto failure;
 	}
 
-	if (PageLocked(page))
-		goto page_locked_wait;
-	if (!PageUptodate(page))
-		goto page_read_error;
+	if (!Page_Uptodate(page)) {
+		lock_page(page);
+		if (!Page_Uptodate(page))
+			goto page_not_uptodate;
+		UnlockPage(page);
+	}
 
 success:
 	/*
-	 * Found the page, need to check sharing and possibly
-	 * copy it over to another page..
+	 * Found the page and have a reference on it, need to check sharing
+	 * and possibly copy it over to another page..
 	 */
 	old_page = page_address(page);
 	if (!no_share) {
@@ -980,6 +1359,7 @@
 			page_cache_free(new_page);
 
 		flush_page_to_ram(old_page);
+		lock_kernel();
 		return old_page;
 	}
 
@@ -989,6 +1369,7 @@
 	copy_page(new_page, old_page);
 	flush_page_to_ram(new_page);
 	page_cache_release(page);
+	lock_kernel();
 	return new_page;
 
 no_cached_page:
@@ -1013,7 +1394,7 @@
 	 * cache.. The page we just got may be useful if we
 	 * can't share, so don't get rid of it here.
 	 */
-	page = find_page(inode, offset);
+	page = __find_get_page(inode, offset, hash);
 	if (page)
 		goto found_page;
 
@@ -1021,19 +1402,24 @@
 	 * Now, create a new page-cache page from the page we got
 	 */
 	page = page_cache_entry(new_page);
-	new_page = 0;
-	add_to_page_cache(page, inode, offset, hash);
+	if (add_to_page_cache_unique(page, inode, offset, hash))
+		goto retry_find;
 
-	if (inode->i_op->readpage(file, page) != 0)
-		goto failure;
+	/*
+	 * Now it's ours and locked, we can do initial IO to it:
+	 */
+	new_page = 0;
 
-	goto found_page;
+page_not_uptodate:
+	error = inode->i_op->readpage(file, page);
 
-page_locked_wait:
-	__wait_on_page(page);
-	if (PageUptodate(page))
+	if (!error) {
+		wait_on_page(page);
+		if (PageError(page))
+			goto page_read_error;
 		goto success;
-	
+	}
+
 page_read_error:
 	/*
 	 * Umm, take care of errors if the page isn't up-to-date.
@@ -1041,12 +1427,14 @@
 	 * because there really aren't any performance issues here
 	 * and we need to check for errors.
 	 */
-	if (inode->i_op->readpage(file, page) != 0)
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+	ClearPageError(page);
+	error = inode->i_op->readpage(file, page);
+	if (error)
 		goto failure;
 	wait_on_page(page);
-	if (PageError(page))
-		goto failure;
-	if (PageUptodate(page))
+	if (Page_Uptodate(page))
 		goto success;
 
 	/*
@@ -1058,6 +1446,8 @@
 	if (new_page)
 		page_cache_free(new_page);
 no_page:
+	lock_kernel();
+no_page_nolock:
 	return 0;
 }
 
@@ -1066,12 +1456,13 @@
  * if the disk is full.
  */
 static inline int do_write_page(struct inode * inode, struct file * file,
-	const char * page, unsigned long offset)
+	const char * page_addr, unsigned long offset)
 {
 	int retval;
 	unsigned long size;
 	loff_t loff = offset;
-	mm_segment_t old_fs;
+	int (*writepage) (struct file *, struct page *);
+	struct page * page;
 
 	size = offset + PAGE_SIZE;
 	/* refuse to extend file size.. */
@@ -1083,12 +1474,21 @@
 			return -EIO;
 	}
 	size -= offset;
-	old_fs = get_fs();
-	set_fs(KERNEL_DS);
 	retval = -EIO;
-	if (size == file->f_op->write(file, (const char *) page, size, &loff))
-		retval = 0;
-	set_fs(old_fs);
+	writepage = inode->i_op->writepage;
+	page = mem_map + MAP_NR(page_addr);
+	lock_page(page);
+
+	if (writepage) {
+		retval = writepage(file, page);
+	} else {
+		mm_segment_t old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		if (size == file->f_op->write(file, page_addr, size, &loff))
+			retval = 0;
+		set_fs(old_fs);
+	}
+	UnlockPage(page);
 	return retval;
 }
 
@@ -1124,9 +1524,7 @@
 		return 0;
 	}
 	
-	down(&inode->i_sem);
 	result = do_write_page(inode, file, (const char *) page, offset);
-	up(&inode->i_sem);
 	fput(file);
 	return result;
 }
@@ -1146,7 +1544,8 @@
 	unsigned long address, unsigned int flags)
 {
 	pte_t pte = *ptep;
-	unsigned long page;
+	unsigned long pageaddr;
+	struct page *page;
 	int error;
 
 	if (!(flags & MS_INVALIDATE)) {
@@ -1158,8 +1557,9 @@
 		flush_cache_page(vma, address);
 		set_pte(ptep, pte_mkclean(pte));
 		flush_tlb_page(vma, address);
-		page = pte_page(pte);
-		atomic_inc(&page_cache_entry(page)->count);
+		pageaddr = pte_page(pte);
+		page = page_cache_entry(pageaddr);
+		get_page(page);
 	} else {
 		if (pte_none(pte))
 			return 0;
@@ -1170,14 +1570,14 @@
 			swap_free(pte_val(pte));
 			return 0;
 		}
-		page = pte_page(pte);
+		pageaddr = pte_page(pte);
 		if (!pte_dirty(pte) || flags == MS_INVALIDATE) {
-			page_cache_free(page);
+			page_cache_free(pageaddr);
 			return 0;
 		}
 	}
-	error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page, 1);
-	page_cache_free(page);
+	error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, pageaddr, 1);
+	page_cache_free(pageaddr);
 	return error;
 }
 
@@ -1338,10 +1738,7 @@
 			struct file * file = vma->vm_file;
 			if (file) {
 				struct dentry * dentry = file->f_dentry;
-				struct inode * inode = dentry->d_inode;
-				down(&inode->i_sem);
 				error = file_fsync(file, dentry);
-				up(&inode->i_sem);
 			}
 		}
 		return error;
@@ -1436,11 +1833,12 @@
 	unsigned long	page_cache = 0;
 	unsigned long	written;
 	long		status;
+	int		err;
 
-	if (file->f_error) {
-		int error = file->f_error;
+	err = file->f_error;
+	if (err) {
 		file->f_error = 0;
-		return error;
+		goto out;
 	}
 
 	written = 0;
@@ -1451,7 +1849,7 @@
 	/*
 	 * Check whether we've reached the file size limit.
 	 */
-	status = -EFBIG;
+	err = -EFBIG;
 	if (pos >= limit) {
 		send_sig(SIGXFSZ, current, 0);
 		goto out;
@@ -1467,6 +1865,8 @@
 		count = limit - pos;
 	}
 
+	unlock_kernel();
+
 	while (count) {
 		unsigned long bytes, pgpos, offset;
 		/*
@@ -1480,29 +1880,36 @@
 			bytes = count;
 
 		hash = page_hash(inode, pgpos);
-		page = __find_page(inode, pgpos, *hash);
+repeat_find:
+		page = __find_lock_page(inode, pgpos, hash);
 		if (!page) {
 			if (!page_cache) {
 				page_cache = page_cache_alloc();
 				if (page_cache)
-					continue;
+					goto repeat_find;
 				status = -ENOMEM;
 				break;
 			}
 			page = page_cache_entry(page_cache);
-			add_to_page_cache(page, inode, pgpos, hash);
+			if (add_to_page_cache_unique(page,inode,pgpos,hash))
+				goto repeat_find;
+
 			page_cache = 0;
 		}
 
-		/* Get exclusive IO access to the page.. */
-		wait_on_page(page);
-		set_bit(PG_locked, &page->flags);
+		/* We have exclusive IO access to the page.. */
+		if (!PageLocked(page)) {
+			PAGE_BUG(page);
+		} else {
+			if (page->owner != (int)current) {
+				PAGE_BUG(page);
+			}
+		}
 
 		status = write_one_page(file, page, offset, bytes, buf);
 
 		/* Mark it unlocked again and drop the page.. */
-		clear_bit(PG_locked, &page->flags);
-		wake_up(&page->wait);
+		UnlockPage(page);
 		page_cache_release(page);
 
 		if (status < 0)
@@ -1519,51 +1926,16 @@
 
 	if (page_cache)
 		page_cache_free(page_cache);
+
+	err = written ? written : status;
+	lock_kernel();
 out:
-	return written ? written : status;
+	return err;
 }
 
 /*
- * Support routines for directory cacheing using the page cache.
- */
-
-/*
- * Finds the page at the specified offset, installing a new page
- * if requested.  The count is incremented and the page is locked.
- *
- * Note: we don't have to worry about races here, as the caller
- * is holding the inode semaphore.
+ * Support routines for directory caching using the page cache.
  */
-unsigned long get_cached_page(struct inode * inode, unsigned long offset,
-				int new)
-{
-	struct page * page;
-	struct page ** hash;
-	unsigned long page_cache = 0;
-
-	hash = page_hash(inode, offset);
-	page = __find_page(inode, offset, *hash);
-	if (!page) {
-		if (!new)
-			goto out;
-		page_cache = page_cache_alloc();
-		if (!page_cache)
-			goto out;
-		clear_page(page_cache);
-		page = page_cache_entry(page_cache);
-		add_to_page_cache(page, inode, offset, hash);
-	}
-	if (atomic_read(&page->count) != 2)
-		printk(KERN_ERR "get_cached_page: page count=%d\n",
-			atomic_read(&page->count));
-	if (test_bit(PG_locked, &page->flags))
-		printk(KERN_ERR "get_cached_page: page already locked!\n");
-	set_bit(PG_locked, &page->flags);
-	page_cache = page_address(page);
-
-out:
-	return page_cache;
-}
 
 /*
  * Unlock and free a page.
@@ -1572,13 +1944,10 @@
 {
 	struct page * page = page_cache_entry(addr);
 
-	if (!test_bit(PG_locked, &page->flags))
-		printk("put_cached_page: page not locked!\n");
-	if (atomic_read(&page->count) != 2)
-		printk("put_cached_page: page count=%d\n", 
-			atomic_read(&page->count));
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	UnlockPage(page);
+	if (page_count(page) != 2)
+		panic("put_cached_page: page count=%d\n", 
+			page_count(page));
 	page_cache_release(page);
 }
 
@@ -1607,11 +1976,13 @@
 
 static inline void make_pio_request(struct file *file,
 				    unsigned long offset,
-				    unsigned long page)
+				    unsigned long pageaddr)
 {
 	struct pio_request *p;
+	struct page *page;
 
-	atomic_inc(&page_cache_entry(page)->count);
+	page = page_cache_entry(pageaddr);
+	get_page(page);
 
 	/* 
 	 * We need to allocate without causing any recursive IO in the
@@ -1634,7 +2005,7 @@
 	
 	p->file   = file;
 	p->offset = offset;
-	p->page   = page;
+	p->page   = pageaddr;
 
 	put_pio_request(p);
 	wake_up(&pio_wait);
@@ -1694,10 +2065,8 @@
 			dentry = p->file->f_dentry;
 			inode = dentry->d_inode;
 			
-			down(&inode->i_sem);
 			do_write_page(inode, p->file,
 				      (const char *) p->page, p->offset);
-			up(&inode->i_sem);
 			fput(p->file);
 			page_cache_free(p->page);
 			kmem_cache_free(pio_request_cache, p);
diff -u --recursive --new-file v2.3.6/linux/mm/memory.c linux/mm/memory.c
--- v2.3.6/linux/mm/memory.c	Tue Jun  8 14:09:57 1999
+++ linux/mm/memory.c	Wed Jun 16 19:26:27 1999
@@ -272,7 +272,7 @@
 				if (vma->vm_flags & VM_SHARED)
 					pte = pte_mkclean(pte);
 				set_pte(dst_pte, pte_mkold(pte));
-				atomic_inc(&mem_map[page_nr].count);
+				get_page(mem_map + page_nr);
 			
 cont_copy_pte_range:		address += PAGE_SIZE;
 				if (address >= end)
@@ -554,7 +554,7 @@
 
 	if (MAP_NR(page) >= max_mapnr)
 		printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address);
-	if (atomic_read(&mem_map[MAP_NR(page)].count) != 1)
+	if (page_count(mem_map + MAP_NR(page)) != 1)
 		printk("mem_map disagrees with %08lx at %08lx\n",page,address);
 	pgd = pgd_offset(tsk->mm,address);
 	pmd = pmd_alloc(pgd, address);
@@ -602,17 +602,17 @@
 	unsigned long address, pte_t *page_table, pte_t pte)
 {
 	unsigned long old_page, new_page;
-	struct page * page_map;
+	struct page * page;
 	
 	new_page = __get_free_page(GFP_USER);
-	/* Did swap_out() unmapped the protected page while we slept? */
+	/* Did swap_out() unmap the protected page while we slept? */
 	if (pte_val(*page_table) != pte_val(pte))
 		goto end_wp_page;
 	old_page = pte_page(pte);
 	if (MAP_NR(old_page) >= max_mapnr)
 		goto bad_wp_page;
 	tsk->min_flt++;
-	page_map = mem_map + MAP_NR(old_page);
+	page = mem_map + MAP_NR(old_page);
 	
 	/*
 	 * We can avoid the copy if:
@@ -622,13 +622,13 @@
 	 *   in which case we can remove the page
 	 *   from the swap cache.
 	 */
-	switch (atomic_read(&page_map->count)) {
+	switch (page_count(page)) {
 	case 2:
-		if (!PageSwapCache(page_map))
+		if (!PageSwapCache(page))
 			break;
-		if (swap_count(page_map->offset) != 1)
+		if (swap_count(page->offset) != 1)
 			break;
-		delete_from_swap_cache(page_map);
+		delete_from_swap_cache(page);
 		/* FallThrough */
 	case 1:
 		flush_cache_page(vma, address);
@@ -650,7 +650,7 @@
 	if (!new_page)
 		goto no_new_page;
 
-	if (PageReserved(page_map))
+	if (PageReserved(page))
 		++vma->vm_mm->rss;
 	copy_cow_page(old_page,new_page);
 	flush_page_to_ram(old_page);
@@ -659,7 +659,7 @@
 	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
 	flush_tlb_page(vma, address);
 	unlock_kernel();
-	__free_page(page_map);
+	__free_page(page);
 	return 1;
 
 bad_wp_page:
@@ -774,7 +774,7 @@
 		if (pte_val(*page_table) != pte_val(entry)) {
 			free_page(pte_page(page));
 		} else {
-			if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) > 1 &&
+			if (page_count(mem_map + MAP_NR(pte_page(page))) > 1 &&
 			    !(vma->vm_flags & VM_SHARED))
 				page = pte_wrprotect(page);
 			++vma->vm_mm->rss;
@@ -858,7 +858,7 @@
 	entry = mk_pte(page, vma->vm_page_prot);
 	if (write_access) {
 		entry = pte_mkwrite(pte_mkdirty(entry));
-	} else if (atomic_read(&mem_map[MAP_NR(page)].count) > 1 &&
+	} else if (page_count(mem_map+MAP_NR(page)) > 1 &&
 		   !(vma->vm_flags & VM_SHARED))
 		entry = pte_wrprotect(entry);
 	set_pte(page_table, entry);
diff -u --recursive --new-file v2.3.6/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.6/linux/mm/mmap.c	Mon Jun  7 11:15:33 1999
+++ linux/mm/mmap.c	Wed Jun 16 19:26:27 1999
@@ -63,7 +63,7 @@
 	    return 1;
 
 	free = buffermem >> PAGE_SHIFT;
-	free += page_cache_size;
+	free += atomic_read(&page_cache_size);
 	free += nr_free_pages;
 	free += nr_swap_pages;
 	free -= (page_cache.min_percent + buffer_mem.min_percent + 2)*num_physpages/100; 
@@ -727,6 +727,10 @@
 	struct mm_struct * mm = current->mm;
 	struct vm_area_struct * vma;
 	unsigned long flags, retval;
+
+	len = PAGE_ALIGN(len);
+	if (!len)
+		return addr;
 
 	/*
 	 * mlock MCL_FUTURE?
diff -u --recursive --new-file v2.3.6/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.3.6/linux/mm/page_alloc.c	Tue May 11 14:37:40 1999
+++ linux/mm/page_alloc.c	Sun Jun 20 16:00:24 1999
@@ -119,33 +119,33 @@
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
 }
 
-void __free_page(struct page *page)
+int __free_page(struct page *page)
 {
-	if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
+	if (!PageReserved(page) && put_page_testzero(page)) {
 		if (PageSwapCache(page))
-			panic ("Freeing swap cache page");
+			PAGE_BUG(page);
 		page->flags &= ~(1 << PG_referenced);
 		free_pages_ok(page - mem_map, 0);
-		return;
+		return 1;
 	}
+	return 0;
 }
 
-void free_pages(unsigned long addr, unsigned long order)
+int free_pages(unsigned long addr, unsigned long order)
 {
 	unsigned long map_nr = MAP_NR(addr);
 
 	if (map_nr < max_mapnr) {
 		mem_map_t * map = mem_map + map_nr;
-		if (PageReserved(map))
-			return;
-		if (atomic_dec_and_test(&map->count)) {
+		if (!PageReserved(map) && put_page_testzero(map)) {
 			if (PageSwapCache(map))
-				panic ("Freeing swap cache pages");
+				PAGE_BUG(map);
 			map->flags &= ~(1 << PG_referenced);
 			free_pages_ok(map_nr, order);
-			return;
+			return 1;
 		}
 	}
+	return 0;
 }
 
 /*
@@ -167,7 +167,7 @@
 				MARK_USED(map_nr, new_order, area); \
 				nr_free_pages -= 1 << order; \
 				EXPAND(ret, map_nr, order, new_order, area); \
-				spin_unlock_irqrestore(&page_alloc_lock, flags); \
+				spin_unlock_irqrestore(&page_alloc_lock,flags);\
 				return ADDRESS(map_nr); \
 			} \
 			prev = ret; \
@@ -186,7 +186,7 @@
 		index += size; \
 		map += size; \
 	} \
-	atomic_set(&map->count, 1); \
+	set_page_count(map, 1); \
 } while (0)
 
 int low_on_memory = 0;
@@ -321,7 +321,7 @@
 	memset(mem_map, 0, start_mem - (unsigned long) mem_map);
 	do {
 		--p;
-		atomic_set(&p->count, 0);
+		set_page_count(p, 0);
 		p->flags = (1 << PG_DMA) | (1 << PG_reserved);
 		init_waitqueue_head(&p->wait);
 	} while (p > mem_map);
diff -u --recursive --new-file v2.3.6/linux/mm/page_io.c linux/mm/page_io.c
--- v2.3.6/linux/mm/page_io.c	Tue May 11 14:37:40 1999
+++ linux/mm/page_io.c	Wed Jun 16 19:26:27 1999
@@ -47,7 +47,7 @@
 #ifdef DEBUG_SWAP
 	printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n",
 		(rw == READ) ? "read" : "write", 
-		entry, (char *) page_address(page), atomic_read(&page->count),
+		entry, (char *) page_address(page), page_count(page),
 		wait ? "wait" : "nowait");
 #endif
 
@@ -105,12 +105,12 @@
 		}
 	}
 	if (rw == READ) {
-		clear_bit(PG_uptodate, &page->flags);
+		ClearPageUptodate(page);
 		kstat.pswpin++;
 	} else
 		kstat.pswpout++;
 
-	atomic_inc(&page->count);
+	get_page(page);
 	if (p->swap_device) {
 		zones[0] = offset;
 		zones_used = 1;
@@ -167,7 +167,7 @@
 				printk("swap_after_unlock_page: lock already cleared\n");
 			wake_up(&lock_queue);
 		}
-		atomic_dec(&page->count);
+		put_page(page);
 		return;
 	}
  	if (!wait) {
@@ -182,23 +182,24 @@
 
  	/* block_size == PAGE_SIZE/zones_used */
  	brw_page(rw, page, dev, zones, block_size, 0);
- 
+
  	/* Note! For consistency we do all of the logic,
  	 * decrementing the page count, and unlocking the page in the
  	 * swap lock map - in the IO completion handler.
  	 */
- 	if (!wait) 
+ 	if (!wait) {
  		return;
+	}
  	wait_on_page(page);
 	/* This shouldn't happen, but check to be sure. */
-	if (atomic_read(&page->count) == 0)
+	if (page_count(page) == 0)
 		printk(KERN_ERR "rw_swap_page: page unused while waiting!\n");
 
 #ifdef DEBUG_SWAP
 	printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n",
 		(rw == READ) ? "read" : "write", 
-		(char *) page_adddress(page), 
-		atomic_read(&page->count));
+		(char *) page_address(page), 
+		page_count(page));
 #endif
 }
 
@@ -238,7 +239,7 @@
 	struct page *page = mem_map + MAP_NR(buf);
 
 	if (page->inode && page->inode != &swapper_inode)
-		panic ("Tried to swap a non-swapper page");
+		PAGE_BUG(page);
 
 	/*
 	 * Make sure that we have a swap cache association for this
@@ -268,23 +269,27 @@
 	struct page *page;
 	
 	page = mem_map + MAP_NR((unsigned long) buffer);
-	wait_on_page(page);
-	set_bit(PG_locked, &page->flags);
-	if (test_and_set_bit(PG_swap_cache, &page->flags)) {
-		printk ("VM: read_swap_page: page already in swap cache!\n");
-		return;
-	}
-	if (page->inode) {
-		printk ("VM: read_swap_page: page already in page cache!\n");
-		return;
-	}
+
+	if (TryLockPage(page))
+		PAGE_BUG(page);
+	if (test_and_set_bit(PG_swap_cache, &page->flags))
+		PAGE_BUG(page);
+	if (page->inode)
+		PAGE_BUG(page);
+	get_page(page);		/* Protect from shrink_mmap() */
 	page->inode = &swapper_inode;
 	page->offset = entry;
-	atomic_inc(&page->count);	/* Protect from shrink_mmap() */
 	rw_swap_page(rw, entry, buffer, 1);
-	atomic_dec(&page->count);
-	page->inode = 0;
-	clear_bit(PG_swap_cache, &page->flags);
+
+	/*
+	 * and now remove it from the pagecache ...
+	 */
+	if (TryLockPage(page))
+		PAGE_BUG(page);
+	PageClearSwapCache(page);
+	remove_inode_page(page);
+	page_cache_release(page);
+	UnlockPage(page);
 }
 
 /*
diff -u --recursive --new-file v2.3.6/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.3.6/linux/mm/swap_state.c	Wed Jan 13 09:54:50 1999
+++ linux/mm/swap_state.c	Sun Jun 20 15:58:20 1999
@@ -25,7 +25,31 @@
  * ensure that any mistaken dereferences of this structure cause a
  * kernel oops.
  */
-struct inode swapper_inode;
+
+static struct inode_operations swapper_inode_operations = {
+	NULL,				/* 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 */
+	NULL,				/* bmap */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	block_flushpage,		/* flushpage */
+	NULL,				/* truncate */
+	NULL,				/* permission */
+	NULL,				/* smap */
+	NULL				/* revalidate */
+};
+
+struct inode swapper_inode = { i_op: &swapper_inode_operations };
 
 #ifdef SWAP_CACHE_INFO
 unsigned long swap_cache_add_total = 0;
@@ -49,20 +73,20 @@
 #endif
 #ifdef DEBUG_SWAP
 	printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
-	       page_address(page), atomic_read(&page->count), entry);
+		   page_address(page), page_count(page), entry);
 #endif
 	if (PageTestandSetSwapCache(page)) {
 		printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
-		       "on page %08lx\n",
-		       page->offset, page_address(page));
+			   "on page %08lx\n",
+			   page->offset, page_address(page));
 		return 0;
 	}
 	if (page->inode) {
 		printk(KERN_ERR "swap_cache: replacing page-cached entry "
-		       "on page %08lx\n", page_address(page));
+			   "on page %08lx\n", page_address(page));
 		return 0;
 	}
-	atomic_inc(&page->count);
+	get_page(page);
 	page->inode = &swapper_inode;
 	page->offset = entry;
 	add_page_to_hash_queue(page, &swapper_inode, entry);
@@ -111,7 +135,7 @@
 	result = 1;
 #ifdef DEBUG_SWAP
 	printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
-	       entry, p->swap_map[offset]);
+		   entry, p->swap_map[offset]);
 #endif
 out:
 	return result;
@@ -127,7 +151,7 @@
 bad_unused:
 	printk(KERN_ERR
 		"swap_duplicate at %8p: entry %08lx, unused page\n", 
-	       __builtin_return_address(0), entry);
+		   __builtin_return_address(0), entry);
 	goto out;
 }
 
@@ -153,7 +177,7 @@
 	retval = p->swap_map[offset];
 #ifdef DEBUG_SWAP
 	printk("DebugVM: swap_count(entry %08lx, count %d)\n",
-	       entry, retval);
+		   entry, retval);
 #endif
 out:
 	return retval;
@@ -163,16 +187,16 @@
 	goto out;
 bad_file:
 	printk(KERN_ERR
-	       "swap_count: entry %08lx, nonexistent swap file!\n", entry);
+		   "swap_count: entry %08lx, nonexistent swap file!\n", entry);
 	goto out;
 bad_offset:
 	printk(KERN_ERR
-	       "swap_count: entry %08lx, offset exceeds max!\n", entry);
+		   "swap_count: entry %08lx, offset exceeds max!\n", entry);
 	goto out;
 bad_unused:
 	printk(KERN_ERR
-	       "swap_count at %8p: entry %08lx, unused page!\n", 
-	       __builtin_return_address(0), entry);
+		   "swap_count at %8p: entry %08lx, unused page!\n", 
+		   __builtin_return_address(0), entry);
 	goto out;
 }
 
@@ -190,18 +214,17 @@
 
 #ifdef DEBUG_SWAP
 	printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
-	       page_address(page), atomic_read(&page->count));
+		   page_address(page), page_count(page));
 #endif
-	PageClearSwapCache (page);
+	PageClearSwapCache(page);
 	remove_inode_page(page);
 }
 
-
 /*
  * This must be called only on pages that have
  * been verified to be in the swap cache.
  */
-void delete_from_swap_cache(struct page *page)
+void __delete_from_swap_cache(struct page *page)
 {
 	long entry = page->offset;
 
@@ -210,13 +233,27 @@
 #endif
 #ifdef DEBUG_SWAP
 	printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
-	       "entry %08lx)\n",
-	       page_address(page), atomic_read(&page->count), entry);
+		   "entry %08lx)\n",
+		   page_address(page), page_count(page), entry);
 #endif
 	remove_from_swap_cache (page);
 	swap_free (entry);
 }
 
+/*
+ * This must be called only on pages that have
+ * been verified to be in the swap cache.
+ */
+void delete_from_swap_cache(struct page *page)
+{
+	lock_page(page);
+
+	__delete_from_swap_cache(page);
+
+	UnlockPage(page);
+	page_cache_release(page);
+}
+
 /* 
  * Perform a free_page(), also freeing any swap cache associated with
  * this page if it is the last user of the page. 
@@ -229,18 +266,18 @@
 	/* 
 	 * If we are the only user, then free up the swap cache. 
 	 */
-	if (PageSwapCache(page) && !is_page_shared(page)) {
+	if (PageSwapCache(page) && !is_page_shared(page))
 		delete_from_swap_cache(page);
-	}
 	
 	__free_page(page);
 }
 
 
 /*
- * Lookup a swap entry in the swap cache.  We need to be careful about
- * locked pages.  A found page will be returned with its refcount
- * incremented.
+ * Lookup a swap entry in the swap cache. A found page will be returned
+ * unlocked and with its refcount incremented - we rely on the kernel
+ * lock getting page table operations atomic even if we drop the page
+ * lock before returning.
  */
 
 struct page * lookup_swap_cache(unsigned long entry)
@@ -251,23 +288,21 @@
 	swap_cache_find_total++;
 #endif
 	while (1) {
-		found = find_page(&swapper_inode, entry);
+		found = find_lock_page(&swapper_inode, entry);
 		if (!found)
 			return 0;
 		if (found->inode != &swapper_inode || !PageSwapCache(found))
 			goto out_bad;
-		if (!PageLocked(found)) {
 #ifdef SWAP_CACHE_INFO
-			swap_cache_find_success++;
+		swap_cache_find_success++;
 #endif
-			return found;
-		}
-		__free_page(found);
-		__wait_on_page(found);
+		UnlockPage(found);
+		return found;
 	}
 
 out_bad:
 	printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
+	UnlockPage(found);
 	__free_page(found);
 	return 0;
 }
@@ -288,7 +323,7 @@
 	
 #ifdef DEBUG_SWAP
 	printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
-	       entry, wait ? ", wait" : "");
+		   entry, wait ? ", wait" : "");
 #endif
 	/*
 	 * Make sure the swap entry is still in use.
@@ -319,12 +354,12 @@
 	if (!add_to_swap_cache(new_page, entry))
 		goto out_free_page;
 
-	set_bit(PG_locked, &new_page->flags);
+	LockPage(new_page);
 	rw_swap_page(READ, entry, (char *) new_page_addr, wait);
 #ifdef DEBUG_SWAP
 	printk("DebugVM: read_swap_cache_async created "
-	       "entry %08lx at %p\n",
-	       entry, (char *) page_address(new_page));
+		   "entry %08lx at %p\n",
+			entry, (char *) page_address(new_page));
 #endif
 	return new_page;
 
@@ -335,3 +370,4 @@
 out:
 	return found_page;
 }
+
diff -u --recursive --new-file v2.3.6/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.3.6/linux/mm/swapfile.c	Mon Jun  7 16:20:36 1999
+++ linux/mm/swapfile.c	Wed Jun 16 19:26:27 1999
@@ -192,7 +192,7 @@
 		return;
 	set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
 	swap_free(entry);
-	atomic_inc(&mem_map[MAP_NR(page)].count);
+	get_page(mem_map + MAP_NR(page));
 	++vma->vm_mm->rss;
 }
 
diff -u --recursive --new-file v2.3.6/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.3.6/linux/mm/vmscan.c	Fri Apr 23 11:07:30 1999
+++ linux/mm/vmscan.c	Wed Jun 16 19:26:27 1999
@@ -157,7 +157,7 @@
 	add_to_swap_cache(page_map, entry);
 	/* We checked we were unlocked way up above, and we
 	   have been careful not to stall until here */
-	set_bit(PG_locked, &page_map->flags);
+	LockPage(page_map);
 
 	/* OK, do a physical asynchronous write to swap.  */
 	rw_swap_page(WRITE, entry, (char *) page, 0);
diff -u --recursive --new-file v2.3.6/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.3.6/linux/net/ipv4/ip_fragment.c	Wed May 26 18:14:37 1999
+++ linux/net/ipv4/ip_fragment.c	Wed Jun 16 19:26:27 1999
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.41 1999/05/27 00:38:07 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.42 1999/06/12 13:11:34 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -218,7 +218,7 @@
 out:
 	/* Nuke the fragment queue. */
 	ip_free(qp);
-	spin_lock(&ipfrag_lock);
+	spin_unlock(&ipfrag_lock);
 }
 
 /* Memory limiting on fragments.  Evictor trashes the oldest 
diff -u --recursive --new-file v2.3.6/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.3.6/linux/net/ipv4/udp.c	Wed Jun  9 14:45:37 1999
+++ linux/net/ipv4/udp.c	Wed Jun 16 19:26:27 1999
@@ -655,11 +655,7 @@
 		if (msg->msg_namelen < sizeof(*usin))
 			return(-EINVAL);
 		if (usin->sin_family != AF_INET) {
-			static int complained;
-			if (!complained++)
-				printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm);
-			if (usin->sin_family)
-				return -EINVAL;
+			return -EINVAL;
 		}
 		ufh.daddr = usin->sin_addr.s_addr;
 		ufh.uh.dest = usin->sin_port;
diff -u --recursive --new-file v2.3.6/linux/net/netsyms.c linux/net/netsyms.c
--- v2.3.6/linux/net/netsyms.c	Wed Jun  9 14:45:37 1999
+++ linux/net/netsyms.c	Wed Jun 16 19:26:27 1999
@@ -377,7 +377,6 @@
 EXPORT_SYMBOL(rtnetlink_links);
 EXPORT_SYMBOL(__rta_fill);
 EXPORT_SYMBOL(rtnetlink_dump_ifinfo);
-EXPORT_SYMBOL(rtnl_wlockct);
 EXPORT_SYMBOL(rtnl);
 EXPORT_SYMBOL(neigh_delete);
 EXPORT_SYMBOL(neigh_add);
