diff -u --recursive --new-file v1.3.59/linux/CREDITS linux/CREDITS
--- v1.3.59/linux/CREDITS	Tue Jan 23 21:15:35 1996
+++ linux/CREDITS	Wed Feb  7 08:04:32 1996
@@ -715,8 +715,8 @@
 S: USA
 
 N: Rick Miller
-E: rick@digalogsys.com
-D: Linux Device Registrar (Major/minor numbers), "au-play", "bwBASIC"
+E: rdmiller@execpc.com
+D: Original Linux Device Registrar (Major/minor numbers), au-play, bwBASIC
 S: S78 W16203 Woods Road
 S: Muskego, Wisconsin 53150
 S: USA
@@ -810,7 +810,9 @@
 
 N: Avery Pennarun
 E: apenwarr@foxnet.net
-D: ARCnet driver, various small changes
+D: ARCnet driver
+D: "make xconfig" improvements
+D: Various minor hacking
 S: RR #5, 497 Pole Line Road
 S: Thunder Bay, Ontario
 S: CANADA P7C 5M9
diff -u --recursive --new-file v1.3.59/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.59/linux/Documentation/Configure.help	Tue Jan 23 21:15:36 1996
+++ linux/Documentation/Configure.help	Wed Feb  7 08:55:33 1996
@@ -649,6 +649,21 @@
   your kernel by about 5 kB. Unless you have Novell computers on your
   local network, say N.
 
+Full internal IPX network
+CONFIG_IPX_INTERN
+  The full internal IPX network enables you to allocate sockets on
+  different virtual nodes of the internal network. This is done by
+  evaluating the field sipx_node of the socket address given to the bind
+  call. So applications should always initialize the node field to 0
+  when binding a socket on the primary network. In this case the socket
+  is assigned the default node that has been given to the kernel when
+  the internal network was created.
+  By enabling the full internal IPX network the cross-forwarding of
+  packets targeted at 'special' sockets to sockets listening on the
+  primary network is disabled. This might break existing applications,
+  especially RIP/SAP daemons. A RIP/SAP daemon that works well with the
+  full internal net can be found on linux01.gwdg.de:/pub/ncpfs.
+
 Appletalk DDP
 CONFIG_ATALK
   Appletalk is the way Apple computers speak to each other on an
diff -u --recursive --new-file v1.3.59/linux/Documentation/cdrom/sbpcd linux/Documentation/cdrom/sbpcd
--- v1.3.59/linux/Documentation/cdrom/sbpcd	Sat Jan  6 19:10:38 1996
+++ linux/Documentation/cdrom/sbpcd	Wed Feb  7 08:17:49 1996
@@ -1,8 +1,8 @@
-This README belongs to release 4.1 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
+This README belongs to release 4.2 or newer of the SoundBlaster Pro
+(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and Teac)
 CD-ROM driver for Linux.
 
-Sbpcd really, really is NOT for ANY IDE/ATAPI drive!
+sbpcd really, really is NOT for ANY IDE/ATAPI drive!
 Not even if you have an "original" SoundBlaster card with an IDE interface!
 So, you better have a look into README.ide if your port address is 0x1F0,
 0x170, 0x1E8, 0x168 or similar.
@@ -46,7 +46,7 @@
 feedback about the bugs within the data functions or a drive (I never saw a
 CD200).
 
-The quad-speed TEAC CD-55A drive is supported, but still does not reach "full
+The quad-speed Teac CD-55A drive is supported, but still does not reach "full
 speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES
 to 64 (it is not recommended to do that for normal "file access" usage, but it
 can speed up things a lot if you use something like "dd" to read from the
@@ -69,7 +69,7 @@
 
 This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
 SoundFX, Mozart, MAD16 ...) and with the "no-sound" cards (Panasonic CI-101P,
-LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
+LaserMate, WDH-7001C, Longshine LCS-6853, Teac ...).
 
 It works with the "configurable" interface "Sequoia S-1000", too, which is 
 used on the Spea Media FX and Ensonic Soundscape sound cards. You have to
@@ -92,15 +92,18 @@
 because the register layout is different between the "SoundBlaster" and the
 "LaserMate" type.
 
-I got a report that the TEAC interface card "I/F E117098" is of type
+I got a report that the Teac interface card "I/F E117098" is of type
 "SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses
 0x300 and above. This is unusual, and it can't get covered by the auto
 probing scheme.
+The Teac 16-bit interface cards (like P/N E950228-00A, default address 0x2C0)
+need the SBPRO 3 setup.
+
 If auto-probing found the drive, the address is correct. The reported type
 may be wrong. A "mount" will give success only if the interface type is set
 right. Playing audio should work with a wrong set interface type, too.
 
-With some TEAC and some CD200 drives I have seen interface cards which seem
+With some Teac and some CD200 drives I have seen interface cards which seem
 to lack the "drive select" lines; always drive 0 gets addressed. To avoid
 "mirror drives" (four drives detected where you only have one) with such
 interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if
@@ -166,7 +169,7 @@
 
 The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with
 CR-56x drives, and currently not more than 500 kB/sec (usually less than
-250 kB/sec) with the TEAC quad speed drives.
+250 kB/sec) with the Teac quad speed drives.
 XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
 
 This release consists of
@@ -193,7 +196,8 @@
           Most "compatible" sound cards (almost all "non-CreativeLabs" cards)
           need "SBPRO 0".
           The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
-          Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup.
+          The Teac 8-bit "no-sound" boards need the "SBPRO 1" setup.
+          The Teac 16-bit "no-sound" boards need the "SBPRO 3" setup.
           All other "no-sound" boards need the "SBPRO 0" setup.
           The Spea Media FX and Ensoniq SoundScape cards need "SBPRO 2".
           sbpcd.c holds some examples in its auto-probe list.
@@ -204,14 +208,17 @@
       a4. Set DISTRIBUTION to 0.
    b. Additionally for 2.a1 and 2.a2, the setup may be done during
       boot time (via the "kernel command line" or "LILO option"):
-          sbpcd=0x230,SoundBlaster
-      or
           sbpcd=0x320,LaserMate
       or
+          sbpcd=0x230,SoundBlaster
+      or
           sbpcd=0x338,SoundScape
+      or
+          sbpcd=0x2C0,Teac16bit
       This is especially useful if you install a fresh distribution.
       If the second parameter is a number, it gets taken as the type
-      setting; 0 is "LaserMate", 1 is "SoundBlaster", 2 is "SoundScape".
+      setting; 0 is "LaserMate", 1 is "SoundBlaster", 2 is "SoundScape",
+      3 is "Teac16bit".
       So, for example
           sbpcd=0x230,1
       is equivalent to
diff -u --recursive --new-file v1.3.59/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v1.3.59/linux/Documentation/devices.tex	Sat Jan  6 19:10:38 1996
+++ linux/Documentation/devices.tex	Wed Feb  7 08:16:47 1996
@@ -42,7 +42,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@storm.net$>$}
-\date{Last revised: January 5, 1996}
+\date{Last revised: February 3, 1996}
 \maketitle
 %
 \noindent
@@ -146,8 +146,8 @@
 \major{  }{}{block}{MCA ESDI hard disk}
 \major{37}{}{char }{IDE tape}
 \major{38}{}{char }{Myricom PCI Myrinet board}
-\major{39}{}{char }{Reserved for ncpfs $<$lendecke@namu01.gwdg.de$>$}
-\major{40}{--41}{}{Unallocated}
+\major{39}{--40}{}{Unallocated}
+\major{41}{}{char}{Yet Another Micro Monitor}
 \major{42}{}{}{Demo/sample use}
 \major{43}{}{char }{isdn4linux virtual modem}
 \major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
@@ -858,11 +858,12 @@
 networking device.
 
 \begin{devicelist}
-\major{39}{}{char }{Reserved for ncpfs $<$lendecke@namu01.gwdg.de$>$}
+\major{39}{--40}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
-\major{40}{--41}{}{Unallocated}
+\major{41}{}{char }{Yet Another Micro Monitor}
+	\minor{0}{/dev/yamm}{Yet Another Micro Monitor}
 \end{devicelist}
 
 \begin{devicelist}
diff -u --recursive --new-file v1.3.59/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v1.3.59/linux/Documentation/devices.txt	Sat Jan  6 19:10:38 1996
+++ linux/Documentation/devices.txt	Wed Feb  7 08:16:47 1996
@@ -2,7 +2,7 @@
 
 	     Maintained by H. Peter Anvin <hpa@storm.net>
 
-		    Last revised: January 5, 1996
+		    Last revised: February 3, 1996
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he lost network access in 1993.  It is a
@@ -581,9 +581,10 @@
 		and "user level packet I/O."  This board is also
 		accessible as a standard networking "eth" device.
 
- 39		Reserved for ncpfs <lendecke@namu01.gwdg.de>
+ 39-40		UNALLOCATED
 
- 40-41		UNALLOCATED
+ 41		Yet Another Micro Monitor
+		  0 = /dev/yamm		Yet Another Micro Monitor
 
  42		Demo/sample use
 
diff -u --recursive --new-file v1.3.59/linux/Documentation/ramdisk.txt linux/Documentation/ramdisk.txt
--- v1.3.59/linux/Documentation/ramdisk.txt	Thu Jan  4 21:54:53 1996
+++ linux/Documentation/ramdisk.txt	Thu Jan 25 21:01:32 1996
@@ -172,7 +172,7 @@
 
 e) Put the kernel onto the floppy
 
-	cat zImage > /dev/fd0
+	dd if=zImage of=/dev/fd0 bs=1k
 
 f) Put the ramdisk image onto the floppy, after the kernel. Use an offset
    that is slightly larger than the kernel, so that you can put another
@@ -181,7 +181,7 @@
    size would be reasonable. Make sure offset+size of ram_image.gz is
    not larger than the total space on your floppy (usually 1440kB).
 
-	dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k skip=400
+	dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k seek=400
 
 g) Use "rdev" to set the boot device, ramdisk offset, prompt flag, etc.
    For ramdisk_start=400, load_ramdisk=1, ramdisk_start=400, one would
diff -u --recursive --new-file v1.3.59/linux/Makefile linux/Makefile
--- v1.3.59/linux/Makefile	Fri Jan 26 01:37:05 1996
+++ linux/Makefile	Wed Feb  7 09:47:28 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 59
+SUBLEVEL = 60
 
 ARCH = i386
 
@@ -12,6 +12,9 @@
 # NOTE! SMP is experimental. See the file Documentation/SMP.txt
 #
 # SMP = 1
+#
+# SMP profiling options
+# SMP_PROF = 1
 
 .EXPORT_ALL_VARIABLES:
 
@@ -20,17 +23,22 @@
 	  else echo sh; fi ; fi)
 TOPDIR	:= $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
 
-HPATH   = $(TOPDIR)/include
-AS	=as
-LD	=ld
-HOSTCC	=gcc -I$(HPATH)
-CC	=gcc -D__KERNEL__ -I$(HPATH)
-MAKE	=make
+HPATH   	= $(TOPDIR)/include
+
+HOSTCC  	=gcc -I$(HPATH)
+HOSTCFLAGS	=
+
+CROSS_COMPILE 	=
+
+AS	=$(CROSS_COMPILE)as
+LD	=$(CROSS_COMPILE)ld
+CC	=$(CROSS_COMPILE)gcc -D__KERNEL__ -I$(HPATH)
 CPP	=$(CC) -E
-AR	=ar
-NM	=nm
-STRIP	=strip
-AWK	=awk
+AR	=$(CROSS_COMPILE)ar
+NM	=$(CROSS_COMPILE)nm
+STRIP	=$(CROSS_COMPILE)strip
+MAKE	=make
+AWK	=gawk
 
 all:	do-it-all
 
@@ -88,7 +96,7 @@
 # standard CFLAGS
 #
 
-CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
+CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce
 
 ifdef CONFIG_CPP
 CFLAGS := $(CFLAGS) -x c++
@@ -96,6 +104,12 @@
 
 ifdef SMP
 CFLAGS += -D__SMP__
+AFLAGS += -D__SMP__
+
+ifdef SMP_PROF
+CFLAGS += -D__SMP_PROF__
+AFLAGS += -D__SMP_PROF__
+endif
 endif
 
 #
@@ -137,9 +151,9 @@
 ifdef SMP
 
 .S.s:
-	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -E -o $*.s $<
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.s $<
 .S.o:
-	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c -o $*.o $<
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
 
 else
 
@@ -175,6 +189,11 @@
 	$(MAKE) -C scripts kconfig.tk
 	wish -f scripts/kconfig.tk
 
+menuconfig: include/linux/version.h symlinks 
+	$(MAKE) -C scripts/lxdialog all
+	$(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.in
+	
+
 config: symlinks
 	$(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
 
@@ -288,11 +307,12 @@
 
 clean:	archclean
 	rm -f kernel/ksyms.lst include/linux/compile.h
-	rm -f core `find . -name '*.[oas]' -print`
+	rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog' -print`
 	rm -f core `find . -type f -name 'core' -print`
 	rm -f vmlinux System.map
 	rm -f .tmp* drivers/sound/configure
 	rm -fr modules/*
+	rm -f submenu*
 
 mrproper: clean
 	rm -f include/linux/autoconf.h include/linux/version.h
@@ -301,11 +321,14 @@
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
+	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
+	rm -f .menuconfig.in
 	rm -f include/asm
 	rm -f .depend `find . -name .depend -print`
 	rm -f .hdepend
 	rm -f $(TOPDIR)/include/linux/modversions.h
 	rm -f $(TOPDIR)/include/linux/modules/*
+	
 
 distclean: mrproper
 	rm -f core `find . \( -name '*.orig' -o -name '*~' -o -name '*.bak' \
diff -u --recursive --new-file v1.3.59/linux/README linux/README
--- v1.3.59/linux/README	Sat Jan  6 19:10:38 1996
+++ linux/README	Wed Feb  7 08:55:33 1996
@@ -105,6 +105,11 @@
    needs bash to work: it will search for bash in $BASH, /bin/bash and
    /bin/sh (in that order), so hopefully one of those is correct. 
 
+ - Alternate configuration commands are:
+	"make menuconfig"  Text based color menus, radiolists & dialogs.
+	"make xconfig"     X windows based configuration tool.
+   
+
 	NOTES on "make config":
 	- having unnecessary drivers will make the kernel bigger, and can
 	  under some circumstances lead to problems: probing for a
diff -u --recursive --new-file v1.3.59/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v1.3.59/linux/arch/i386/Makefile	Tue Jan  2 16:46:21 1996
+++ linux/arch/i386/Makefile	Thu Jan 25 21:01:33 1996
@@ -25,9 +25,13 @@
 
 ifdef CONFIG_KERNEL_ELF
 
-LD=ld -m elf_i386
+LD=$(CROSS_COMPILE)ld -m elf_i386
 CPP=$(CC) -E -D__ELF__
-OBJDUMP =objdump
+OBJDUMP =$(CROSS_COMPILE)objdump
+ENCAPS=$(CROSS_COMPILE)encaps
+AS86    =$(CROSS_COMPILE)as86 -0 -a
+AS386   =$(CROSS_COMPILE)as86 -3
+LD86    =$(CROSS_COMPILE)ld86 -0
 OBJDUMP_FLAGS=-k -q 
 ZLDFLAGS=-e startup_32 
 LDFLAGS=-e stext
diff -u --recursive --new-file v1.3.59/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile
--- v1.3.59/linux/arch/i386/boot/Makefile	Fri Nov 17 08:42:24 1995
+++ linux/arch/i386/boot/Makefile	Thu Jan 25 21:01:33 1996
@@ -8,15 +8,12 @@
 # Copyright (C) 1994 by Linus Torvalds
 #
 
-AS86    =as86 -0 -a
-LD86    =ld86 -0
-
 ifdef CONFIG_KERNEL_ELF
-CFLAGS := $(CFLAGS) -D__BFD__
+HOSTCFLAGS := $(HOSTCFLAGS) -D__BFD__
 endif
 
 ifdef SMP
-CFLAGS := $(CFLAGS) -D__SMP__
+HOSTCFLAGS := $(HOSTCFLAGS) -D__SMP__
 endif
 
 zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
@@ -45,7 +42,7 @@
 	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 tools/build: tools/build.c
-	$(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
+	$(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include
 
 setup: setup.o
 	$(LD86) -s -o $@ $<
diff -u --recursive --new-file v1.3.59/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile
--- v1.3.59/linux/arch/i386/boot/compressed/Makefile	Sun Dec 17 11:43:09 1995
+++ linux/arch/i386/boot/compressed/Makefile	Thu Jan 25 21:01:33 1996
@@ -47,7 +47,7 @@
 	rm -f $$tmppiggy $$tmppiggy.gz; \
 	$(OBJDUMP) $(OBJDUMP_FLAGS) -o $(IMAGE_OFFSET) $(SYSTEM) > $$tmppiggy; \
 	gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
-	encaps $(TARGET) piggy.o $$tmppiggy.gz $(INPUT_DATA) $(INPUT_LEN); \
+	$(ENCAPS) $(TARGET) piggy.o $$tmppiggy.gz $(INPUT_DATA) $(INPUT_LEN); \
 	rm -f $$tmppiggy $$tmppiggy.gz
 else
 
diff -u --recursive --new-file v1.3.59/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.59/linux/arch/i386/config.in	Tue Jan 23 21:15:36 1996
+++ linux/arch/i386/config.in	Thu Jan 25 21:01:33 1996
@@ -8,7 +8,6 @@
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
-  MODULES=y
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
   bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
 fi
diff -u --recursive --new-file v1.3.59/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.59/linux/arch/i386/defconfig	Fri Jan 26 01:37:05 1996
+++ linux/arch/i386/defconfig	Wed Feb  7 10:06:46 1996
@@ -25,7 +25,7 @@
 CONFIG_M586=y
 
 #
-# block devices
+# Block devices
 #
 CONFIG_BLK_DEV_FD=y
 # CONFIG_BLK_DEV_RAM is not set
@@ -61,6 +61,10 @@
 # CONFIG_TCP_NAGLE_OFF is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
+
+#
+#  
+#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
 # CONFIG_AX25 is not set
@@ -108,7 +112,9 @@
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_XIA_FS is not set
+CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
+# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
@@ -119,7 +125,7 @@
 # CONFIG_SYSV_FS is not set
 
 #
-# character devices
+# Character devices
 #
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
diff -u --recursive --new-file v1.3.59/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v1.3.59/linux/arch/i386/kernel/Makefile	Tue Jan 23 21:15:36 1996
+++ linux/arch/i386/kernel/Makefile	Wed Feb  7 08:55:34 1996
@@ -7,18 +7,13 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-AS86    =as86 -0 -a
-AS386	=as86 -3
-LD86    =ld86 -0
-
-
 #.S.s:
 #	$(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
 
 ifdef SMP
 
 .S.o:
-	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $< -o $*.o
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
 else
 .S.o:
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
diff -u --recursive --new-file v1.3.59/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v1.3.59/linux/arch/i386/kernel/entry.S	Sun Jan 14 16:30:10 1996
+++ linux/arch/i386/kernel/entry.S	Wed Feb  7 08:55:34 1996
@@ -148,18 +148,31 @@
 
 */
 
+#ifndef __SMP_PROF__
+
+#define SMP_PROF_A
+#define SMP_PROF_B 
+
+#else
+
+#define SMP_PROF_A movl $0,SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4);
+#define SMP_PROF_B incl SYMBOL_NAME(smp_spins_syscall)(,%eax,4); \
+        incl SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4);
+#endif
+
 #define	ENTER_KERNEL \
 	pushl %eax; \
 	pushl %edx; \
 	pushfl; \
 	cli; \
 	GET_PROCESSOR_ID \
+	SMP_PROF_A \
 1:	lock; \
 	btsl $0, SYMBOL_NAME(kernel_flag); \
 	jnc 3f; \
 	cmpb SYMBOL_NAME(active_kernel_processor), %al; \
 	je 4f; \
-2:	incl SYMBOL_NAME(smp_spins); \
+2:	SMP_PROF_B \
 	btl %al, SYMBOL_NAME(smp_invalidate_needed); \
 	jnc 5f; \
 	lock; \
diff -u --recursive --new-file v1.3.59/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v1.3.59/linux/arch/i386/kernel/irq.c	Wed Nov  8 07:11:29 1995
+++ linux/arch/i386/kernel/irq.c	Wed Feb  7 08:55:34 1996
@@ -36,6 +36,10 @@
 static unsigned char cache_21 = 0xff;
 static unsigned char cache_A1 = 0xff;
 
+#ifdef __SMP_PROF__
+static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},};
+#endif
+
 void disable_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
@@ -188,15 +192,78 @@
 /*
  *	Linus - should you add NMI counts here ?????
  */
-#ifdef __SMP__
+#ifdef __SMP_PROF__
 	len+=sprintf(buf+len, "IPI: %8lu received\n",
 		ipi_count);
-	len+=sprintf(buf+len, "LCK: %8lu spins\n",
-		smp_spins);
 #endif		
 	return len;
 }
 
+#ifdef __SMP_PROF__
+
+int get_smp_prof_list(char *buf) {
+        int i,j, len = 0;
+        struct irqaction * action = irq_action;
+        unsigned long sum_spins = 0;
+        unsigned long sum_spins_syscall = 0;
+        unsigned long sum_spins_sys_idle = 0;
+        unsigned long sum_smp_idle_count = 0;
+
+        for (i=0;i<=smp_num_cpus;i++) {
+          sum_spins+=smp_spins[i];
+          sum_spins_syscall+=smp_spins_syscall[i];
+          sum_spins_sys_idle+=smp_spins_sys_idle[i];
+          sum_smp_idle_count+=smp_idle_count[i];
+        }
+
+	len += sprintf(buf+len,"CPUS: %10i \n", 
+		0==smp_num_cpus?1:smp_num_cpus);
+        len += sprintf(buf+len,"            SUM ");
+        for (i=0;i<smp_num_cpus;i++)
+          len += sprintf(buf+len,"        P%1d ",i);
+        len += sprintf(buf+len,"\n");
+        for (i = 0 ; i < NR_IRQS ; i++, action++) {
+                if (!action->handler)
+                        continue;
+                len += sprintf(buf+len, "%3d: %10d ",
+                        i, kstat.interrupts[i]);
+                for (j=0;j<smp_num_cpus;j++)
+                  len+=sprintf(buf+len, "%10d ",int_count[j][i]);
+                len += sprintf(buf+len, "%c %s\n",
+                        (action->flags & SA_INTERRUPT) ? '+' : ' ',
+                        action->name);
+        }
+        len+=sprintf(buf+len, "LCK: %10lu",
+                sum_spins);
+        for (i=0;i<smp_num_cpus;i++)
+          len+=sprintf(buf+len," %10lu",smp_spins[i]);
+        len +=sprintf(buf+len,"   spins from int\n");
+
+        len+=sprintf(buf+len, "LCK: %10lu",
+                sum_spins_syscall);
+        for (i=0;i<smp_num_cpus;i++)
+          len+=sprintf(buf+len," %10lu",smp_spins_syscall[i]);
+        len +=sprintf(buf+len,"   spins from syscall\n");
+
+        len+=sprintf(buf+len, "LCK: %10lu",
+                sum_spins_sys_idle);
+        for (i=0;i<smp_num_cpus;i++)
+          len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[i]);
+        len +=sprintf(buf+len,"   spins from sysidle\n");
+        len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count);
+        for (i=0;i<smp_num_cpus;i++)
+          len+=sprintf(buf+len," %10lu",smp_idle_count[i]);
+        len +=sprintf(buf+len,"   idle ticks\n");
+
+        len+=sprintf(buf+len, "IPI: %10lu   received\n",
+                ipi_count);
+
+        return len;
+}
+#endif 
+
+
+
 /*
  * do_IRQ handles IRQ's that have been installed without the
  * SA_INTERRUPT flag: it uses the full signal-handling return
@@ -213,6 +280,9 @@
 #endif
 
 	kstat.interrupts[irq]++;
+#ifdef __SMP_PROF__
+        int_count[smp_processor_id()][irq]++;
+#endif
 	if (action->flags & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
 	action->handler(irq, regs);
@@ -233,6 +303,9 @@
 #endif
 
 	kstat.interrupts[irq]++;
+#ifdef __SMP_PROF__
+        int_count[smp_processor_id()][irq]++;
+#endif
 	if (action->flags & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
 	action->handler(irq, NULL);
diff -u --recursive --new-file v1.3.59/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.3.59/linux/arch/i386/kernel/process.c	Wed Dec 27 09:12:12 1995
+++ linux/arch/i386/kernel/process.c	Wed Feb  7 08:55:34 1996
@@ -130,6 +130,10 @@
 {
 	if(current->pid != 0)
 		return -EPERM;
+#ifdef __SMP_PROF__
+	smp_spins_sys_idle[smp_processor_id()]+=
+	  smp_spins_syscall_cur[smp_processor_id()];
+#endif
 	current->counter= -100;
 	schedule();
 	return 0;
diff -u --recursive --new-file v1.3.59/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.59/linux/arch/i386/kernel/smp.c	Thu Jan  4 21:54:53 1996
+++ linux/arch/i386/kernel/smp.c	Wed Feb  7 08:55:34 1996
@@ -63,9 +63,19 @@
 volatile unsigned char active_kernel_processor = NO_PROC_ID;	/* Processor holding kernel spinlock		*/
 volatile unsigned long kernel_counter=0;		/* Number of times the processor holds the lock		*/
 volatile unsigned long syscall_count=0;			/* Number of times the processor holds the syscall lock	*/
-volatile unsigned long smp_spins=0;			/* Count of cycles wasted to spinning			*/
 
 volatile unsigned long ipi_count;			/* Number of IPI's delivered				*/
+#ifdef __SMP_PROF__
+volatile unsigned long smp_spins[NR_CPUS]={0};          /* Count interrupt spins 				*/
+volatile unsigned long smp_spins_syscall[NR_CPUS]={0};  /* Count syscall spins                   		*/
+volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall                 */
+volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle 				*/
+volatile unsigned long smp_idle_count[1+NR_CPUS]={0,};	/* Count idle ticks					*/
+#endif
+#if defined (__SMP_PROF__)
+volatile unsigned long smp_idle_map=0;			/* Map for idle processors 				*/
+#endif
+
 
 /* 
  *	Checksum an MP configuration block.
@@ -473,8 +483,10 @@
 	/*
 	 *	Map the local APIC into kernel space
 	 */
-	
-	apic_reg = vremap(0xFEE00000,4096);
+
+	/* Mapping on non-Intel conforming platforms is a bad move. */
+	if (1<cpu_present_map)	
+		apic_reg = vremap(0xFEE00000,4096);
 	
 	
 	if(apic_reg == NULL)
@@ -518,45 +530,63 @@
 			cfg|=(1<<8);		/* Enable APIC */
 			apic_write(APIC_SPIV,cfg);
 			
-			/*
-			 *	This gunge sends an IPI (Inter Processor Interrupt) to the
-			 *	processor we wish to wake. When the startup IPI is received
-			 *	the target CPU does a real mode jump to the stack base.
-			 */
-			
-			cfg=apic_read(APIC_ICR2);
-			cfg&=0x00FFFFFF;
-			apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i));			/* Target chip     	*/
-			cfg=apic_read(APIC_ICR);
-			cfg&=~0xFDFFF	;							/* Clear bits 		*/
-			cfg|=APIC_DEST_FIELD|APIC_DEST_DM_STARTUP|(((unsigned long)stack)>>12);	/* Boot on the stack 	*/		
-			apic_write(APIC_ICR, cfg);						/* Kick the second 	*/
-			udelay(10);								/* Masses of time 	*/
-			cfg=apic_read(APIC_ESR);
-			if(cfg&4)		/* Send accept error */
-				printk("Processor refused startup request.\n");
-			else
+			for(timeout=0;timeout<50000;timeout++)
 			{
-				for(timeout=0;timeout<50000;timeout++)
+				/*
+				 *	This gunge sends an IPI (Inter Processor Interrupt) to the
+				 *	processor we wish to wake. When the startup IPI is received
+				 *	the target CPU does a real mode jump to the stack base.
+				 *
+				 *	We do the following
+				 *
+				 *	Time 0	: Send a STARTUP IPI (This is all that is needed).
+				 *	Time 20000  : Send an INIT IPI for broken boards.
+				 *	Time 20001  : Send a second STARTUP IPI for broken boards.
+				 *
+				 *	We can't just do INIT/STARTUP - that breaks the correctly
+				 *	implemented ASUS boards.
+				 */
+			
+				if(timeout==20000)
 				{
-					if(cpu_callin_map[0]&(1<<i))
-						break;				/* It has booted */
-					udelay(100);				/* Wait 5s total for a response */
+					cfg=apic_read(APIC_ICR2);
+					cfg&=0x00FFFFFF;
+					apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i));			/* Target chip     	*/
+					cfg=apic_read(APIC_ICR);
+					cfg&=~0xFDFFF	;							/* Clear bits 		*/
+					cfg|=APIC_DEST_DM_INIT;                                                 /* INIT the CPU         */
+					apic_write(APIC_ICR, cfg);                                              /* Kick the second      */
+					printk("\nBuggy motherboard ?, trying an INIT IPI: ");
+					udelay(10);                                                             /* Masses of time       */
 				}
-				if(cpu_callin_map[0]&(1<<i))
-					cpucount++;
-				else
+				if(timeout==0||timeout==20001)
 				{
-					/*
-					 *	At this point we should set up a BIOS warm start and try
-					 *	a RESTART IPI. The 486+82489 MP pair don't support STARTUP IPI's
-					 */
-					if(*((unsigned char *)8192)==0xA5)
-						printk("Stuck ??\n");
-					else
-						printk("Not responding.\n");
-					cpu_present_map&=~(1<<i);
+					cfg=apic_read(APIC_ICR);
+					cfg&=~0xFDFFF   ;                                                       /* Clear bits           */
+					cfg|=APIC_DEST_FIELD|APIC_DEST_DM_STARTUP|(((unsigned long)stack)>>12);	/* Boot on the stack 	*/		
+					apic_write(APIC_ICR, cfg);						/* Kick the second 	*/
+					udelay(10);								/* Masses of time 	*/
+					cfg=apic_read(APIC_ESR);
+					if(cfg&4)		/* Send accept error */
+						printk("Processor refused startup request.\n");
 				}
+				if(cpu_callin_map[0]&(1<<i))
+					break;				/* It has booted */
+				udelay(100);				/* Wait 5s total for a response */
+			}
+			if(cpu_callin_map[0]&(1<<i))
+				cpucount++;
+			else
+			{
+				/*
+				 *	At this point we should set up a BIOS warm start and try
+				 *	a RESTART IPI. The 486+82489 MP pair don't support STARTUP IPI's
+				 */
+				if(*((unsigned char *)8192)==0xA5)
+					printk("Stuck ??\n");
+				else
+					printk("Not responding.\n");
+				cpu_present_map&=~(1<<i);
 			}
 		}
 	}
@@ -797,13 +827,14 @@
 
 void smp_reschedule_irq(int cpl, struct pt_regs *regs)
 {
+#ifdef DEBUGGING_SMP_RESCHED
 	static int ct=0;
 	if(ct==0)
 	{
 		printk("Beginning scheduling on CPU#%d\n",smp_processor_id());
 		ct=1;
 	}
-	
+#endif	
 	if(smp_processor_id()!=active_kernel_processor)
 		panic("SMP Reschedule on CPU #%d, but #%d is active.\n",
 			smp_processor_id(), active_kernel_processor);
diff -u --recursive --new-file v1.3.59/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.59/linux/drivers/block/Config.in	Tue Jan 23 21:15:36 1996
+++ linux/drivers/block/Config.in	Thu Jan 25 21:01:33 1996
@@ -2,7 +2,7 @@
 # Block device driver configuration
 #
 mainmenu_option next_comment
-comment 'block devices'
+comment 'Block devices'
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
diff -u --recursive --new-file v1.3.59/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.59/linux/drivers/block/floppy.c	Sat Jan  6 19:10:38 1996
+++ linux/drivers/block/floppy.c	Wed Feb  7 08:55:34 1996
@@ -3248,6 +3248,14 @@
 			return 0;
 
 		case FDFMTBEG:
+			LOCK_FDC(drive,1);
+			CALL(poll_drive(1, FD_RAW_NEED_DISK));
+			ret = UDRS->flags;
+			process_fd_request();
+			if(ret & FD_VERIFY)
+				return -ENODEV;
+			if(!(ret & FD_DISK_WRITABLE))
+				return -EROFS;
 			return 0;
 		case FDFMTTRK:
 			if (UDRS->fd_ref != 1)
@@ -3671,30 +3679,33 @@
 		return FDC_82077;	/* Revised 82077AA passes all the tests */
 	}
 	switch (reply_buffer[0] >> 5) {
-	case 0x0:
-		output_byte(FD_SAVE);
-		r = result();
-		if (r != 17) {
-			printk("FDC %d init: SAVE: unexpected return of %d bytes.\n",fdc,r);
-			return FDC_UNKNOWN;
-		}
-		if (!(reply_buffer[0] & 0x40)) {
-			printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+		case 0x0:
+			output_byte(FD_SAVE);
+			r = result();
+			if (r != 17) {
+				printk("FDC %d init: SAVE: unexpected return of %d bytes.\n", fdc, r);
+				return FDC_UNKNOWN;
+			}
+			if (!(reply_buffer[0] & 0x40)) {
+				printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+				return FDC_82078;
+			}
+			/* Either a 82078-1 or a 82078SL running at 5Volt */
+			printk("FDC %d is a 82078-1.\n",fdc);
+			return FDC_82078_1;
+		case 0x1:
+			printk("FDC %d is a 44pin 82078\n",fdc);
 			return FDC_82078;
-		}
-		/* Either a 82078-1 or a 82078SL running at 5Volt */
-		printk("FDC %d is a 82078-1.\n",fdc);
-		return FDC_82078_1;
-	case 0x1:
-		printk("FDC %d is a 44pin 82078\n",fdc);
-		return FDC_82078;
-	case 0x3:
-		printk("FDC %d is a National Semiconductor PC87306\n",fdc);
-		return FDC_87306;
-	default:
-		printk("FDC %d init: Unknown 82077 variant, PARTID = %d.\n",
-		       fdc, reply_buffer[0] >> 5);
-		return FDC_82077_UNKN;
+		case 0x2:
+			printk("FDC %d is a S82078B\n", fdc);
+			return FDC_S82078B;
+		case 0x3:
+			printk("FDC %d is a National Semiconductor PC87306\n", fdc);
+			return FDC_87306;
+		default:
+			printk("FDC %d init: 82077 variant with PARTID=%d.\n",
+			       fdc, reply_buffer[0] >> 5);
+			return FDC_82077_UNKN;
 	}
 } /* get_fdc_version */
 
diff -u --recursive --new-file v1.3.59/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v1.3.59/linux/drivers/cdrom/cdu31a.c	Sun Dec 17 11:43:13 1995
+++ linux/drivers/cdrom/cdu31a.c	Thu Jan 25 21:01:32 1996
@@ -1959,6 +1959,7 @@
 static int
 sony_get_subchnl_info(long arg)
 {
+   int err;
    struct cdrom_subchnl schi;
 
 
@@ -1972,8 +1973,9 @@
       return -EIO;
    }
 
-   verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
-   verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+   err = verify_area(VERIFY_READ, (char *) arg, sizeof(schi)) ||
+         verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+   if (err) return err;
 
    memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
    
diff -u --recursive --new-file v1.3.59/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v1.3.59/linux/drivers/cdrom/sbpcd.c	Sat Jan  6 19:10:39 1996
+++ linux/drivers/cdrom/sbpcd.c	Wed Feb  7 08:17:50 1996
@@ -1,20 +1,19 @@
 /*
- *  sbpcd.c   CD-ROM device driver for the whole family of IDE-style
- *            Kotobuki/Matsushita/Panasonic CR-5xx drives for
- *            SoundBlaster ("Pro" or "16 ASP" or compatible) cards
- *            and for "no-sound" interfaces like Lasermate and the
- *            Panasonic CI-101P.
+ *  sbpcd.c   CD-ROM device driver for the whole family of traditional,
+ *            non-ATAPI IDE-style Matsushita/Panasonic CR-5xx drives.
+ *            Works with SoundBlaster compatible cards and with "no-sound"
+ *            interface cards like Lasermate, Panasonic CI-101P, Teac, ...
  *            Also for the Longshine LCS-7260 drive.
  *            Also for the IBM "External ISA CD-Rom" drive.
- *            Also for the CreativeLabs CD200 drive (but I still need some
- *            detailed bug reports).
+ *            Also for the CreativeLabs CD200 drive.
  *            Also for the TEAC CD-55A drive.
- *            Not for Sanyo drives (but sjcd is there...).
- *            Not for any other Funai drives than E2550UA (="CD200" with "F").
- *            Not for Vertos drives yet.
+ *            Also for the ECS-AT "Vertos 100" drive.
+ *            Not for Sanyo drives (but for the H94A, sjcd is there...).
+ *            Not for any other Funai drives than the CD200 types
+ *            (sometimes labelled E2550UA or MK4015 or E2880UA).
  */
 
-#define VERSION "v4.1 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v4.2 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
  *
@@ -62,7 +61,7 @@
  *       Flow control should be exact now.
  *       Don't occupy the SbPro IRQ line (not needed either); will
  *       live together with Hannu Savolainen's sndkit now.
- *	 Speeded up data transfer to 150 kB/sec, with help from Kai
+ *       Speeded up data transfer to 150 kB/sec, with help from Kai
  *       Makisara, the "provider" of the "mt" tape utility.
  *       Give "SpinUp" command if necessary.
  *       First steps to support up to 4 drives (but currently only one).
@@ -264,13 +263,24 @@
  *       to the Longshine LCS-7260. Give feedback if you can - I never saw
  *       such a drive, and I have no specs.
  *
+ *  4.2  Support for Teac 16-bit interface cards. Can't get auto-detected,
+ *       so you have to jumper your card to 0x2C0. Still not 100% - come
+ *       in contact if you can give qualified feedback.
+ *       Use loglevel KERN_NOTICE with printk(). If you get annoyed by a
+ *       flood of unwanted messages and the accompanied delay, try to read
+ *       my documentation. Especially the Linux CDROM drivers have to do an
+ *       important job for the newcomers, so the "distributed" version has
+ *       to fit some special needs. Since generations, the flood of messages
+ *       is user-configurable (even at runtime), but to get aware of this, one
+ *       needs a special mental quality: the ability to read.
+ *       
+ *
  *  TODO
  *
  *     disk change detection
  *     synchronize multi-activity
  *        (data + audio + ioctl + disk change, multiple drives)
  *     implement "read all subchannel data" (96 bytes per frame)
- *     check if CDROMPLAYMSF can cause a hang
  *
  *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
  *     elaborated speed-up experiments (and the fabulous results!), for
@@ -372,6 +382,7 @@
 	0x300, 0, /* CI-101P (default), WDH-7001C (default),
 		     Galaxy (default), Reveal (one default) */
 	0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
+	0x2C0, 3, /* Teac 16-bit cards */
 	0x260, 1, /* OmniCD */
 	0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
 		     Longshine LCS-6853 (default) */
@@ -496,10 +507,11 @@
 static int sbpcd_ioaddr = CDROM_PORT;	/* default I/O base address */
 static int sbpro_type = SBPRO;
 static unsigned char setup_done = 0;
+static unsigned char f_16bit = 0;
+static unsigned char do_16bit = 0;
 static int CDo_command, CDo_reset;
 static int CDo_sel_i_d, CDo_enable;
 static int CDi_info, CDi_status, CDi_data;
-static int MIXER_addr, MIXER_data;
 static struct cdrom_msf msf;
 static struct cdrom_ti ti;
 static struct cdrom_tochdr tochdr;
@@ -519,6 +531,8 @@
 static const char *str_sp_l = "spea";
 static const char *str_ss = "SoundScape";
 static const char *str_ss_l = "soundscape";
+static const char *str_t16 = "Teac16bit";
+static const char *str_t16_l = "teac16bit";
 const char *type;
 
 #if !(SBPCD_ISSUE-1)
@@ -721,7 +735,7 @@
 	
 	msgnum++;
 	if (msgnum>99) msgnum=0;
-	sprintf(buf, KERN_INFO "%s-%d [%02d]:  ", major_name, d, msgnum);
+	sprintf(buf, KERN_NOTICE "%s-%d [%02d]:  ", major_name, d, msgnum);
 	va_start(args, fmt);
 	vsprintf(&buf[18], fmt, args);
 	va_end(args);
@@ -1202,10 +1216,12 @@
 {
 #undef CMDT_TRIES
 #define CMDT_TRIES 1000
+#define TEST_FALSE_FF 1
 	
 	static int cc_DriveReset(void);
-	int i, j, l, ntries;
-	
+	int i, j, l=0, m, ntries;
+	long flags;
+
 	D_S[d].error_state=0;
 	D_S[d].b3=0;
 	D_S[d].b4=0;
@@ -1217,6 +1233,12 @@
 	OUT(CDo_sel_i_d,0);
 	OUT(CDo_enable,D_S[d].drv_sel);
 	i=inb(CDi_status);
+	do_16bit=0;
+	if ((f_16bit)&&(!(i&0x80)))
+	{
+		do_16bit=1;
+		msg(DBG_TEA,"cmd_out_T: do_16bit set.\n");
+	}
 	if (!(i&s_not_result_ready))
 	do
 	{
@@ -1226,15 +1248,13 @@
 		msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j);
 	}
 	while (!(i&s_not_result_ready));
-	cli();
+	save_flags(flags); cli();
 	for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]);
-	sti();
+	restore_flags(flags);
 	for (ntries=CMDT_TRIES;ntries>0;ntries--)
 	{
-		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ);
-#if 1
-		OUT(CDo_sel_i_d,0);
-#endif
+		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ); /* fixme */
+		OUT(CDo_sel_i_d,1);
 		i=inb(CDi_status);
 		if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
 		{
@@ -1244,10 +1264,24 @@
 			{
 				l=0;
 				do
-				{
-					infobuf[l++]=inb(CDi_data);
-					i=inb(CDi_status);
-				}
+                                {
+                                        if (do_16bit)
+                                        {
+                                                i=inw(CDi_data);
+                                                infobuf[l++]=i&0x0ff;
+                                                infobuf[l++]=i>>8;
+#if TEST_FALSE_FF
+                                                if ((l==2)&&(infobuf[0]==0x0ff))
+                                                {
+                                                        infobuf[0]=infobuf[1];
+                                                        l=1;
+                                                        msg(DBG_TEA,"cmd_out_T: do_16bit: false first byte!\n");
+                                                }
+#endif TEST_FALSE_FF
+                                        }
+                                        else infobuf[l++]=inb(CDi_data);
+                                        i=inb(CDi_status);
+                                }
 				while (!(i&s_not_data_ready));
 				for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
 				msgbuf[j*3]=0;
@@ -1255,16 +1289,18 @@
 			}
 			else
 			{
-				msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X !!!!!!!!!!!!!!!!!!!!\n", drvcmd[0]);
+				msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X!\n",
+                                    drvcmd[0]);
 				j=0;
 				do
 				{
-					i=inb(CDi_data);
-					j++;
-					i=inb(CDi_status);
+                                        if (do_16bit) i=inw(CDi_data);
+                                        else i=inb(CDi_data);
+                                        j++;
+                                        i=inb(CDi_status);
 				}
 				while (!(i&s_not_data_ready));
-				msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes.\n", j);
+				msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes/words.\n", j);
 				fatal_err++;
 			}
 		}
@@ -1272,17 +1308,23 @@
 		if (!(i&s_not_result_ready))
 		{
 			OUT(CDo_sel_i_d,0);
-			l=0;
+			if (drvcmd[0]==CMDT_DISKINFO) m=l;
+			else m=0;
 			do
 			{
-				infobuf[l++]=inb(CDi_info);
+				infobuf[m++]=inb(CDi_info);
 				i=inb(CDi_status);
 			}
 			while (!(i&s_not_result_ready));
-			for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+			for (j=0;j<m;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
 			msgbuf[j*3]=0;
 			msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf);
-			if (infobuf[0]!=0x02) return (l); /* info length */
+			if (drvcmd[0]==CMDT_DISKINFO)
+                        {
+                                infobuf[0]=infobuf[l];
+                                if (infobuf[0]!=0x02) return (l); /* data length */
+                        }
+			else if (infobuf[0]!=0x02) return (m); /* info length */
 			do
 			{
 				++recursion;
@@ -1578,7 +1620,7 @@
 {
 	int i;
 	
-	if (fam0LV_drive) return (-3);
+	if (fam0LV_drive) return (0);
 	clr_cmdbuf();
 	response_count=0;
 	if (fam1_drive)
@@ -2506,6 +2548,7 @@
 	}
 	i=cmd_out();
 	if (i<0) return (i);
+	if ((famT_drive)&&(i<response_count)) return (-100-i);
 	if ((fam1_drive)||(fam2_drive)||(fam0LV_drive))
 		D_S[d].xa_byte=infobuf[0];
 	if (fam2_drive)
@@ -2620,6 +2663,7 @@
 	}
 	i=cmd_out();
 	if (i<0) return (i);
+	if ((famT_drive)&&(i<response_count)) return (-100-i);
 	if ((fam1_drive)||(fam0LV_drive))
 	{
 		D_S[d].TocEnt_nixbyte=infobuf[0];
@@ -2846,6 +2890,7 @@
 		drvcmd[9]=0x40;
 		i=cmd_out();
 		if (i<0) return (i);
+		if (i<response_count) return (-100-i);
 		D_S[d].first_session=infobuf[2];
 		D_S[d].last_session=infobuf[3];
 		D_S[d].track_of_last_session=infobuf[6];
@@ -2970,7 +3015,8 @@
 {
 	int i;
 	
-	msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de:\n");
+	msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de\n");
+	msg(DBG_INF, "(don't mail if you are not using the actual kernel):\n");
 	msg(DBG_INF, "%s\n", VERSION);
 	msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n",
 	    CDo_command, type, D_S[d].drive_model, D_S[d].drv_id);
@@ -4190,7 +4236,10 @@
 			{
 			}
 			msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
-			for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+			flags_cmd_out=f_putcmd;
+			response_count=0;
+			i=cmd_out();
+			if (i<0) msg(DBG_INF,"error giving READ AUDIO command: %0d\n", i);
 			sbp_sleep(0);
 			msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
 			for (frame=1;frame<2 && !error_flag; frame++)
@@ -4228,7 +4277,8 @@
 				error_flag=0;
 				p = D_S[d].aud_buf;
 				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-				insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
+				if (do_16bit) insw(CDi_data, p, read_audio.nframes*(CD_FRAMESIZE_RAW>>1));
+				else insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
 				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
 				data_retrying = 0;
 			}
@@ -4544,17 +4594,10 @@
 		drvcmd[7]=(D_S[d].sbp_read_frames>>8)&0x0ff;
 		drvcmd[8]=D_S[d].sbp_read_frames&0x0ff;
 	}
-#ifdef OLD
-	SBPCD_CLI;
-	for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-	if (famT_drive) for (i=7;i<10;i++) OUT(CDo_command,drvcmd[i]);
-	SBPCD_STI;
-#else
 	flags_cmd_out=f_putcmd;
 	response_count=0;
 	i=cmd_out();
 	if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
-#endif OLD
 	return;
 }
 /*==========================================================================*/
@@ -4653,9 +4696,14 @@
 		msg(DBG_000, "sbp_data: beginning to read.\n");
 		p = D_S[d].sbp_buf + frame *  CD_FRAMESIZE;
 		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-		if (cmd_type==READ_M2) insb(CDi_data, xa_head_buf, CD_XA_HEAD);
-		insb(CDi_data, p, CD_FRAMESIZE);
-		if (cmd_type==READ_M2) insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
+		if (cmd_type==READ_M2)
+                        if (do_16bit) insw(CDi_data, xa_head_buf, CD_XA_HEAD>>1);
+                        else insb(CDi_data, xa_head_buf, CD_XA_HEAD);
+		if (do_16bit) insw(CDi_data, p, CD_FRAMESIZE>>1);
+		else insb(CDi_data, p, CD_FRAMESIZE);
+		if (cmd_type==READ_M2)
+                        if (do_16bit) insw(CDi_data, xa_tail_buf, CD_XA_TAIL>>1);
+                        else insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
 		D_S[d].sbp_current++;
 		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
 		if (cmd_type==READ_M2)
@@ -4689,12 +4737,13 @@
 				j=0;
 				do
 				{
-					i=inb(CDi_data);
+					if (do_16bit) i=inw(CDi_data);
+					else i=inb(CDi_data);
 					j++;
 					i=inb(CDi_status);
 				}
 				while (!(i&s_not_data_ready));
-				msg(DBG_TEA, "=============too much data (%d bytes)=================.\n", j);
+				msg(DBG_TEA, "==========too much data (%d bytes/words)==============.\n", j);
 			}
 			if (!(i&s_not_result_ready))
 			{
@@ -4981,7 +5030,9 @@
  *             or
  *                 sbpcd=0x300,LaserMate
  *             or
- *                 sbpcd=0x330,SoundScape
+ *                 sbpcd=0x338,SoundScape
+ *             or
+ *                 sbpcd=0x2C0,Teac16bit
  *
  * (upper/lower case sensitive here - but all-lowercase is ok!!!).
  *
@@ -5001,12 +5052,14 @@
 	msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
 	sbpro_type=0; /* default: "LaserMate" */
 	if (p[0]>1) sbpro_type=p[2];
-	if (!strcmp(s,str_sb)) sbpro_type=1;
+	else if (!strcmp(s,str_sb)) sbpro_type=1;
 	else if (!strcmp(s,str_sb_l)) sbpro_type=1;
 	else if (!strcmp(s,str_sp)) sbpro_type=2;
 	else if (!strcmp(s,str_sp_l)) sbpro_type=2;
 	else if (!strcmp(s,str_ss)) sbpro_type=2;
 	else if (!strcmp(s,str_ss_l)) sbpro_type=2;
+	else if (!strcmp(s,str_t16)) sbpro_type=3;
+	else if (!strcmp(s,str_t16_l)) sbpro_type=3;
 	if (p[0]>0) sbpcd_ioaddr=p[1];
 	
 	CDo_command=sbpcd_ioaddr;
@@ -5015,11 +5068,15 @@
 	CDo_sel_i_d=sbpcd_ioaddr+1;
 	CDo_reset=sbpcd_ioaddr+2;
 	CDo_enable=sbpcd_ioaddr+3; 
-	if (sbpro_type==1)
+	f_16bit=0;
+	if ((sbpro_type==1)||(sbpro_type==3))
 	{
-		MIXER_addr=sbpcd_ioaddr-0x10+0x04;
-		MIXER_data=sbpcd_ioaddr-0x10+0x05;
 		CDi_data=sbpcd_ioaddr;
+		if (sbpro_type==3)
+                {
+                        f_16bit=1;
+                        sbpro_type=1;
+                }
 	}
 	else CDi_data=sbpcd_ioaddr+2;
 }
@@ -5142,6 +5199,7 @@
 		}
 		if (sbpcd[port_index+1]==2) type=str_sp;
 		else if (sbpcd[port_index+1]==1) type=str_sb;
+		else if (sbpcd[port_index+1]==3) type=str_t16;
 		else type=str_lm;
 		sbpcd_setup(type, addr);
 #if DISTRIBUTION
@@ -5239,19 +5297,12 @@
 	
 	/*
 	 * Turn on the CD audio channels.
-	 * For "compatible" soundcards (with "SBPRO 0" or "SBPRO 2"), the addresses
-	 * are obtained from SOUND_BASE (see sbpcd.h).
+	 * The addresses are obtained from SOUND_BASE (see sbpcd.h).
 	 */
-	if ((sbpro_type==1) || (SOUND_BASE))
-	{
-		if (sbpro_type!=1)
-		{
-			MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
-			MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
-		}
-		OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
-		OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
-	}
+#if SOUND_BASE
+	OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
+	OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
+#endif SOUND_BASE
 	
 	if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
 	{
diff -u --recursive --new-file v1.3.59/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v1.3.59/linux/drivers/char/ChangeLog	Sun Dec 17 11:43:17 1995
+++ linux/drivers/char/ChangeLog	Wed Feb  7 08:55:35 1996
@@ -1,3 +1,15 @@
+Wed Jan 10 10:08:00 1996    <tytso@rsts-11.mit.edu>
+
+	* serial.c (block_til_ready): Remove race condition which happened
+		if a hangup condition happened during the setup of the
+		UART, before rs_open() called block_til_ready().  This
+		caused the info->count counter to be erroneously
+		decremented.
+
+	* serial.c (startup, rs_open): Remove race condition that could
+		cuase a memory leak of one page.  (Fortunately, both race
+		conditions were relatively rare in practice.)
+
 Tue Dec  5 13:21:27 1995    <tytso@rsts-11.mit.edu>
 
 	* serial.c (check_modem_status, rs_ioctl): Support the new
diff -u --recursive --new-file v1.3.59/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.59/linux/drivers/char/Config.in	Tue Jan 23 21:15:39 1996
+++ linux/drivers/char/Config.in	Wed Feb  7 08:55:35 1996
@@ -2,7 +2,7 @@
 # Character device configuration
 #
 mainmenu_option next_comment
-comment 'character devices'
+comment 'Character devices'
 
 tristate 'Cyclades async mux support' CONFIG_CYCLADES
 bool 'Stallion multiport serial support' CONFIG_STALDRV
@@ -37,8 +37,8 @@
   bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
 fi
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
-if [ "$CONFIG_WATCHDOG" = "y" ]; then
-  bool '   WDT Watchdog timer' CONFIG_WDT
+if [ "$CONFIG_WATCHDOG" != "n" ]; then
+  tristate '   WDT Watchdog timer' CONFIG_WDT
   if [ "$CONFIG_WDT" = "y" ]; then
      bool '       WDT501 features' CONFIG_WDT_501
      if [ "$CONFIG_WDT_501" = "y" ]; then
diff -u --recursive --new-file v1.3.59/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.59/linux/drivers/char/Makefile	Tue Jan 23 21:15:39 1996
+++ linux/drivers/char/Makefile	Wed Feb  7 08:55:35 1996
@@ -98,6 +98,16 @@
 # This is not modularized, so if configured then "mouse.c" will be resident
 endif
 
+ifeq ($(CONFIG_WDT),y)
+M = y
+L_OBJS += wdt.o
+else
+  ifeq ($(CONFIG_WDT),m)
+    M_OBJS += wdt.o
+    MM = m
+  endif
+endif
+
 ifdef CONFIG_QIC02_TAPE
 L_OBJS += tpqic02.o 
 endif
diff -u --recursive --new-file v1.3.59/linux/drivers/char/mouse.c linux/drivers/char/mouse.c
--- v1.3.59/linux/drivers/char/mouse.c	Tue Jan 23 21:15:40 1996
+++ linux/drivers/char/mouse.c	Wed Feb  7 08:55:35 1996
@@ -14,6 +14,9 @@
  * of the mouse drivers, as they are now completely independent. Linus.
  *
  * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ *
+ * Fixed a failing symbol register to free the device registration
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
  */
 
 #include <linux/module.h>
@@ -137,5 +140,10 @@
 		return -EIO;
 	}
 
-	return register_symtab(&mouse_syms);
+	if(register_symtab(&mouse_syms)!=0)
+	{
+		unregister_chrdev(MOUSE_MAJOR, "mouse");
+		return -EIO;
+	}
+	return 0;
 }
diff -u --recursive --new-file v1.3.59/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.3.59/linux/drivers/char/serial.c	Sun Dec 17 11:43:18 1995
+++ linux/drivers/char/serial.c	Wed Feb  7 08:55:36 1996
@@ -887,23 +887,32 @@
 	unsigned long flags;
 	int	retval;
 	void (*handler)(int, struct pt_regs *);
+	unsigned long page;
 
-	if (info->flags & ASYNC_INITIALIZED)
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		restore_flags(flags);
 		return 0;
+	}
 
 	if (!info->port || !info->type) {
 		if (info->tty)
 			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		restore_flags(flags);
 		return 0;
 	}
-
-	if (!info->xmit_buf) {
-		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-		if (!info->xmit_buf)
-			return -ENOMEM;
-	}
-
-	save_flags(flags); cli();
+	if (info->xmit_buf)
+		free_page(page);
+	else
+		info->xmit_buf = (unsigned char *) page;
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
@@ -2207,7 +2216,8 @@
 	 * If the device is in the middle of being closed, then block
 	 * until it's done, and then try again.
 	 */
-	if (info->flags & ASYNC_CLOSING) {
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
 		interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
@@ -2271,7 +2281,10 @@
 	printk("block_til_ready before block: ttys%d, count = %d\n",
 	       info->line, info->count);
 #endif
-	info->count--;
+	cli();
+	if (!tty_hung_up_p(filp)) 
+		info->count--;
+	sti();
 	info->blocked_open++;
 	while (1) {
 		cli();
@@ -2333,6 +2346,7 @@
 {
 	struct async_struct	*info;
 	int 			retval, line;
+	unsigned long		page;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
@@ -2350,9 +2364,13 @@
 	info->tty = tty;
 
 	if (!tmp_buf) {
-		tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-		if (!tmp_buf)
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
 			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
 	}
 	
 	/*
diff -u --recursive --new-file v1.3.59/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v1.3.59/linux/drivers/char/softdog.c	Sat Jan  6 19:10:39 1996
+++ linux/drivers/char/softdog.c	Wed Feb  7 08:55:36 1996
@@ -39,7 +39,7 @@
  *	If the timer expires..
  */
  
-static void watchdog_fire(long data)
+static void watchdog_fire(unsigned long data)
 {
 	extern void hard_reset_now(void);
 	hard_reset_now();
diff -u --recursive --new-file v1.3.59/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v1.3.59/linux/drivers/char/tpqic02.c	Tue Jan 23 21:15:40 1996
+++ linux/drivers/char/tpqic02.c	Thu Jan 25 21:01:32 1996
@@ -2585,7 +2585,8 @@
 	if (c == DDIOCSDBG) {
 		if (!suser())
 			return -EPERM;
-		verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
+		error = verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
+		if (error) return error;
 		c = get_user_long((int *) ioarg);
 		if (c==0) {
 			QIC02_TAPE_DEBUG = 0;
diff -u --recursive --new-file v1.3.59/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v1.3.59/linux/drivers/char/wdt.c	Sun Jan 14 16:30:10 1996
+++ linux/drivers/char/wdt.c	Wed Feb  7 08:55:36 1996
@@ -15,8 +15,9 @@
  *	herein by reference. The driver is provided without warranty or 
  *	support.
  *
- *	Release 0.04.
+ *	Release 0.05.
  *
+ *	Some changes by Dave Gregorich to fix modularisation and minor bugs.
  */
 
 #include <linux/config.h>
@@ -110,7 +111,7 @@
 	return -ESPIPE;
 }
 
-static int wdt_write(struct inode *inode, struct file *file, char *buf, int count)
+static int wdt_write(struct inode *inode, struct file *file, const char *buf, int count)
 {
 	/* Write a watchdog value */
 	inb_p(WDT_DC);
@@ -138,7 +139,7 @@
 				return err;
 			c*=11;
 			c/=15;
-			cp=c;
+			cp=c+7;
 			memcpy_tofs(buf,&cp,1);
 			return 1;
 		default:
@@ -217,12 +218,14 @@
 	&wdt_fops
 };
 
+#ifdef CONFIG_WDT_501
 static struct mouse temp_mouse=
 {
 	TEMP_MINOR,
 	"temperature",
 	&wdt_fops
 };
+#endif
 
 #ifdef MODULE
 
@@ -263,7 +266,9 @@
 		return -EIO;
 	}
 	mouse_register(&wdt_mouse);
+#ifdef CONFIG_WDT_501	
 	mouse_register(&temp_mouse);
+#endif	
 	request_region(io, 8, "wdt501");
 	return 0;
 }
diff -u --recursive --new-file v1.3.59/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.3.59/linux/drivers/net/3c509.c	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/3c509.c	Wed Feb  7 08:55:36 1996
@@ -640,15 +640,15 @@
 			printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
 		}
 	}
-	if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) 
-	{
-		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
-	} 
-	else if (dev->flags&IFF_PROMISC) 
+	if (dev->flags&IFF_PROMISC) 
 	{
 		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
 			 ioaddr + EL3_CMD);
 	}
+	else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) 
+	{
+		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+	} 
 	else
 		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
diff -u --recursive --new-file v1.3.59/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v1.3.59/linux/drivers/net/arcnet.c	Sun Jan 14 16:30:11 1996
+++ linux/drivers/net/arcnet.c	Wed Feb  7 08:55:36 1996
@@ -968,7 +968,8 @@
 }
 
 
-/* Setup a struct device for ARCnet.  This should really be in net_init.c
+/*
+ * Setup a struct device for ARCnet.  This should really be in net_init.c
  * but since there are three different ARCnet devices ANYWAY... <gargle>
  *
  * Actually, the whole idea of having all this kernel-dependent stuff (ie.
@@ -976,23 +977,25 @@
  *
  * Intelligent defaults?!  Nah.
  */
+
 void arcnet_setup(struct device *dev)
 {
 	int i;
 	for (i=0; i<DEV_NUMBUFFS; i++)
 		skb_queue_head_init(&dev->buffs[i]);
 
-	dev->broadcast[0]=0x00;	/* broadcasts on ARCnet are address 0 */
-	dev->addr_len=1;
-	dev->type=ARPHRD_ARCNET;
+	dev->broadcast[0]	= 0x00;	/* broadcasts on ARCnet are address 0 */
+	dev->addr_len		= 1;
+	dev->type		= ARPHRD_ARCNET;
+	dev->tx_queue_len 	= 30;	/* Fairly long queue, arcnet is quite speedy */
 
 	/* New-style flags. */
-	dev->flags	= IFF_BROADCAST;
-	dev->family	= AF_INET;
-	dev->pa_addr	= 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask	= 0;
-	dev->pa_alen	= 4;
+	dev->flags		= IFF_BROADCAST;
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr 	= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= 4;
 }
 
 
diff -u --recursive --new-file v1.3.59/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v1.3.59/linux/drivers/net/eql.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/eql.c	Wed Feb  7 08:55:36 1996
@@ -25,12 +25,16 @@
  * Inspirations:
  *   The Harried and Overworked Alan Cox
  * Conspiracies:
- *   The Alan Cox and Mike McLagan plot to get someone else to do the code, which
- *   turned out to be me.
+ *   The Alan Cox and Mike McLagan plot to get someone else to do the code, 
+ *   which turned out to be me.
  */
 
 /*
  * $Log: eql.c,v $
+ * Revision 3.13  1996/01/21  15:17:18  alan
+ * tx_queue_len changes.
+ * reformatted.
+ *
  * Revision 3.12  1995/03/22  21:07:51  anarchy
  * Added suser() checks on configuration.
  * Moved header file.
@@ -202,990 +206,879 @@
    ---------------------------------------------------------
    */
 
-int
-eql_init(struct device *dev)
+int eql_init(struct device *dev)
 {
-  static unsigned version_printed = 0;
-  /* static unsigned num_masters     = 0; */
-  equalizer_t *eql = 0;
-  int i;
-
-  if ( version_printed++ == 0 && eql_debug > 0)
-    printk(version);
-
-  /* Initialize the device structure. */
-  dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL);
-  if (dev->priv == NULL)
-      return -ENOMEM;
-  memset (dev->priv, 0, sizeof (equalizer_t));
-  eql = (equalizer_t *) dev->priv;
-
-  eql->stats = kmalloc (sizeof (struct enet_statistics), GFP_KERNEL);
-  if (eql->stats == NULL) {
-      kfree(dev->priv);
-      dev->priv = NULL;
-      return -ENOMEM;
-  }
-  memset (eql->stats, 0, sizeof (struct enet_statistics));
-
-  init_timer (&eql->timer);
-  eql->timer.data     = (unsigned long) dev->priv;
-  eql->timer.expires  = jiffies+EQL_DEFAULT_RESCHED_IVAL;
-  eql->timer.function = &eql_timer;
-  eql->timer_on       = 0;
-
-  dev->open		= eql_open;
-  dev->stop		= eql_close;
-  dev->do_ioctl         = eql_ioctl;
-  dev->hard_start_xmit  = eql_slave_xmit;
-  dev->get_stats	= eql_get_stats;
-  
-  /* Fill in the fields of the device structure with ethernet-generic values.
-     This should be in a common file instead of per-driver.  */
-
-  for (i = 0; i < DEV_NUMBUFFS; i++)
-    skb_queue_head_init(&dev->buffs[i]);
+	static unsigned version_printed = 0;
+	/* static unsigned num_masters     = 0; */
+	equalizer_t *eql = 0;
+	int i;
+
+	if ( version_printed++ == 0 && eql_debug > 0)
+		printk(version);
+	/*
+	 *	Initialize the device structure. 
+	 */
+	dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset (dev->priv, 0, sizeof (equalizer_t));
+	eql = (equalizer_t *) dev->priv;
 
-  dev->hard_header    = eql_header; 
-  dev->rebuild_header = eql_rebuild_header;
-
-  /* now we undo some of the things that eth_setup does that we don't like */
-  dev->mtu        = EQL_DEFAULT_MTU;	/* set to 576 in eql.h */
-  dev->flags      = IFF_MASTER;
+	eql->stats = kmalloc (sizeof (struct enet_statistics), GFP_KERNEL);
+	if (eql->stats == NULL) 
+	{
+		kfree(dev->priv);
+		dev->priv = NULL;
+		return -ENOMEM;
+	}
+	memset (eql->stats, 0, sizeof (struct enet_statistics));
 
-  dev->family     = AF_INET;
-  dev->pa_addr    = 0;
-  dev->pa_brdaddr = 0;
-  dev->pa_mask    = 0;
-  dev->pa_alen    = 4;
+	init_timer (&eql->timer);
+	eql->timer.data     	= (unsigned long) dev->priv;
+	eql->timer.expires  	= jiffies+EQL_DEFAULT_RESCHED_IVAL;
+	eql->timer.function 	= &eql_timer;
+	eql->timer_on       	= 0;
+
+	dev->open		= eql_open;
+	dev->stop		= eql_close;
+	dev->do_ioctl		= eql_ioctl;
+	dev->hard_start_xmit	= eql_slave_xmit;
+	dev->get_stats		= eql_get_stats;
+  
+  	/*
+  	 *	Fill in the fields of the device structure with 
+	 *	eql-generic values. This should be in a common 
+	 *	file instead of per-driver.  
+	 */
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	dev->hard_header	= eql_header; 
+	dev->rebuild_header	= eql_rebuild_header;
+
+	/*
+	 *	Now we undo some of the things that eth_setup does
+	 * 	that we don't like 
+	 */
+	 
+	dev->mtu        	= EQL_DEFAULT_MTU;	/* set to 576 in eql.h */
+	dev->flags      	= IFF_MASTER;
+
+	dev->family     	= AF_INET;
+	dev->pa_addr    	= 0;
+	dev->pa_brdaddr 	= 0;
+	dev->pa_mask    	= 0;
+	dev->pa_alen    	= 4;
 
-  dev->type       = ARPHRD_SLIP;
+	dev->type       	= ARPHRD_SLIP;
+	dev->tx_queue_len 	= 5;		/* Hands them off fast */
 
-  return 0;
+	return 0;
 }
-
 
-static
-int
-eql_open(struct device *dev)
+static int eql_open(struct device *dev)
 {
-  equalizer_t *eql = (equalizer_t *) dev->priv;
-  slave_queue_t *new_queue;
+	equalizer_t *eql = (equalizer_t *) dev->priv;
+	slave_queue_t *new_queue;
 
 #ifdef EQL_DEBUG
-  if (eql_debug >= 5)
-    printk ("%s: open\n", dev->name);
+	if (eql_debug >= 5)
+		printk ("%s: open\n", dev->name);
 #endif
 
-   new_queue = eql_new_slave_queue (dev);
+	new_queue = eql_new_slave_queue (dev);
     
-    if (new_queue != 0)
-      {
-	new_queue->master_dev = dev;
-	eql->queue = new_queue;
-	eql->queue->lock = 0;
-	eql->min_slaves = 1;
-	eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
-
-	printk ("%s: adding timer\n", dev->name);
-	eql->timer_on = 1;
-	add_timer (&eql->timer);
+	if (new_queue != 0)
+	{
+		new_queue->master_dev = dev;
+		eql->queue = new_queue;
+		eql->queue->lock = 0;
+		eql->min_slaves = 1;
+		eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
+
+		printk ("%s: adding timer\n", dev->name);
+		eql->timer_on = 1;
+		add_timer (&eql->timer);
 
-	MOD_INC_USE_COUNT;
-	return 0;
-      }
-  return 1;
+		MOD_INC_USE_COUNT;
+		return 0;
+	}
+	return 1;
 }
 
 
-static
-int
-eql_close(struct device *dev)
+static int eql_close(struct device *dev)
 {
-  equalizer_t *eql = (equalizer_t *) dev->priv;
+	equalizer_t *eql = (equalizer_t *) dev->priv;
 
 #ifdef EQL_DEBUG
-  if ( eql_debug >= 5)
-    printk ("%s: close\n", dev->name);
+	if ( eql_debug >= 5)
+		printk ("%s: close\n", dev->name);
 #endif
-  /* The timer has to be stopped first before we start hacking away
-     at the data structure it scans every so often... */
-  printk ("%s: stopping timer\n", dev->name);
-  eql->timer_on = 0;
-  del_timer (&eql->timer);
+	/*
+	 *	The timer has to be stopped first before we start hacking away
+	 *	at the data structure it scans every so often... 
+	 */
 
-  eql_delete_slave_queue (eql->queue);
+#ifdef EQL_DEBUG
+	printk ("%s: stopping timer\n", dev->name);
+#endif	
+	eql->timer_on = 0;
+	del_timer (&eql->timer);
 
-  MOD_DEC_USE_COUNT;
+	eql_delete_slave_queue (eql->queue);
 
-  return 0;
+	MOD_DEC_USE_COUNT;
+	return 0;
 }
-
 
-static
-int
-eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+
+static int eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {  
-  if(!suser() && cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG)
-  	return -EPERM;
-  switch (cmd)
-    {
-    case EQL_ENSLAVE:
-      return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data);
-    case EQL_EMANCIPATE:
-      return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data);
-
-    case EQL_GETSLAVECFG:
-      return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
-    case EQL_SETSLAVECFG:
-      return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
-
-    case EQL_GETMASTRCFG:
-      return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data);
-    case EQL_SETMASTRCFG:
-      return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data);
-
-    default:
-      return -EOPNOTSUPP;
-    }
+	if(!suser() && cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG)
+	  	return -EPERM;
+	switch (cmd)
+	{
+		case EQL_ENSLAVE:
+			return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data);
+		case EQL_EMANCIPATE:
+			return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data);
+		case EQL_GETSLAVECFG:
+			return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+		case EQL_SETSLAVECFG:
+			return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+		case EQL_GETMASTRCFG:
+			return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+		case EQL_SETMASTRCFG:
+			return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+		default:
+			return -EOPNOTSUPP;
+	}
 }
-
 
-static
-int
-eql_slave_xmit(struct sk_buff *skb, struct device *dev)
-{
-  equalizer_t *eql = (equalizer_t *) dev->priv;
-  struct device *slave_dev = 0;
-  slave_t *slave;
-
-  if (skb == NULL)
-    {
-      return 0;
-    }
 
-  eql_schedule_slaves (eql->queue);
+static int eql_slave_xmit(struct sk_buff *skb, struct device *dev)
+{
+	equalizer_t *eql = (equalizer_t *) dev->priv;
+	struct device *slave_dev = 0;
+	slave_t *slave;
+
+	if (skb == NULL)
+		return 0;
+
+	eql_schedule_slaves (eql->queue);
   
-  slave_dev = eql_best_slave_dev (eql->queue);
-  slave = eql_best_slave (eql->queue); 
+	slave_dev = eql_best_slave_dev (eql->queue);
+	slave = eql_best_slave (eql->queue); 
 
-  if ( slave_dev != 0 )
-    {
+	if ( slave_dev != 0 )
+	{
 #ifdef EQL_DEBUG
-      if (eql_debug >= 100)
-	printk ("%s: %d slaves xmitng %ld B %s\n", 
-		dev->name, eql_number_slaves (eql->queue), skb->len,
-		slave_dev->name);
+		if (eql_debug >= 100)
+			printk ("%s: %d slaves xmitng %ld B %s\n", 
+				dev->name, eql_number_slaves (eql->queue), skb->len,
+				slave_dev->name);
 #endif
-      
-      dev_queue_xmit (skb, slave_dev, 1);
-      eql->stats->tx_packets++;
-      slave->bytes_queued += skb->len; 
-    }
-  else
-    {
-      /* The alternative for this is the return 1 and have
-         dev_queue_xmit just queue it up on the eql's queue. */
-
-      eql->stats->tx_dropped++;
-      dev_kfree_skb(skb, FREE_WRITE);
-    }	  
-  return 0;
+		dev_queue_xmit (skb, slave_dev, 1);
+		eql->stats->tx_packets++;
+		slave->bytes_queued += skb->len; 
+	}
+	else
+	{
+		/*
+		 *	The alternative for this is the return 1 and have
+		 *	dev_queue_xmit just queue it up on the eql's queue. 
+		 */
+
+		eql->stats->tx_dropped++;
+		dev_kfree_skb(skb, FREE_WRITE);
+	}	  
+	return 0;
 }
-
 
-static
-struct enet_statistics *
-eql_get_stats(struct device *dev)
-{
-  equalizer_t *eql = (equalizer_t *) dev->priv;
 
-  return eql->stats;
+static struct enet_statistics * eql_get_stats(struct device *dev)
+{
+	equalizer_t *eql = (equalizer_t *) dev->priv;
+	return eql->stats;
 }
 
 
-static 
-int 
-eql_header(struct sk_buff *skb, struct device *dev, 
+static int  eql_header(struct sk_buff *skb, struct device *dev, 
 	   unsigned short type, void *daddr, void *saddr, 
 	   unsigned len)
 {
-  return 0;
+	return 0;
 }
 
 
-static 
-int 
-eql_rebuild_header(void *buff, struct device *dev, 
+static int eql_rebuild_header(void *buff, struct device *dev, 
 		   unsigned long raddr, struct sk_buff *skb)
 {
-  return 0;
+	return 0;
 }
 
+/*
+ *	Private ioctl functions
+ */
 
-
-
-/* private ioctl functions
-   -----------------------------------------------------------------
-   */
-
-static int 
-eql_enslave(struct device *dev, slaving_request_t *srqp)
+static int eql_enslave(struct device *dev, slaving_request_t *srqp)
 {
-  struct device *master_dev;
-  struct device *slave_dev;
-  slaving_request_t srq;
-  int err;
-
-  err = verify_area(VERIFY_READ, (void *)srqp, sizeof (slaving_request_t));
-  if (err) return err;
-
-  memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
+	struct device *master_dev;
+	struct device *slave_dev;
+	slaving_request_t srq;
+	int err;
+
+	err = verify_area(VERIFY_READ, (void *)srqp, sizeof (slaving_request_t));
+	if (err) 
+		return err;
+	memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
 
 #ifdef EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: enslave '%s' %ld bps\n", dev->name, 
-	    srq.slave_name, srq.priority);
-#endif
-  
-  master_dev = dev;		/* for "clarity" */
-  slave_dev  = dev_get (srq.slave_name);
+	if (eql_debug >= 20)
+		printk ("%s: enslave '%s' %ld bps\n", dev->name, 
+			srq.slave_name, srq.priority);
+#endif  
+	master_dev = dev;		/* for "clarity" */
+	slave_dev  = dev_get (srq.slave_name);
 
-  if (master_dev != 0 && slave_dev != 0)
-    {
-      if (! eql_is_master (slave_dev)  &&   /* slave is not a master */
-	  ! eql_is_slave (slave_dev)      ) /* slave is not already a slave */
+	if (master_dev != 0 && slave_dev != 0)
 	{
-	  slave_t *s = eql_new_slave ();
-	  equalizer_t *eql = (equalizer_t *) master_dev->priv;
-
-	  s->dev = slave_dev;
-	  s->priority = srq.priority;
-	  s->priority_bps = srq.priority;
-	  s->priority_Bps = srq.priority / 8;
-
-	  slave_dev->flags |= IFF_SLAVE;
-
-	  eql_insert_slave (eql->queue, s);
-
-	  return 0;
+		if (! eql_is_master (slave_dev)  &&   /* slave is not a master */
+			! eql_is_slave (slave_dev)      ) /* slave is not already a slave */
+		{
+			slave_t *s = eql_new_slave ();
+			equalizer_t *eql = (equalizer_t *) master_dev->priv;
+			s->dev = slave_dev;
+			s->priority = srq.priority;
+			s->priority_bps = srq.priority;
+			s->priority_Bps = srq.priority / 8;
+			slave_dev->flags |= IFF_SLAVE;
+			eql_insert_slave (eql->queue, s);
+			return 0;
+		}
+		return -EINVAL;
 	}
-      return -EINVAL;
-    }
-  return -EINVAL;
+	return -EINVAL;
 }
-
 
+static int eql_emancipate(struct device *dev, slaving_request_t *srqp)
+{
+	struct device *master_dev;
+	struct device *slave_dev;
+	slaving_request_t srq;
+	int err;
 
-static 
-int 
-eql_emancipate(struct device *dev, slaving_request_t *srqp)
-{
-  struct device *master_dev;
-  struct device *slave_dev;
-  slaving_request_t srq;
-  int err;
-
-  err = verify_area(VERIFY_READ, (void *)srqp, sizeof (slaving_request_t));
-  if (err) return err;
-
-  memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
+	err = verify_area(VERIFY_READ, (void *)srqp, sizeof (slaving_request_t));
+	if (err) 
+		return err;
 
+	memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
 #ifdef EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name);
+	if (eql_debug >= 20)
+		printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name);
 #endif
+	master_dev = dev;		/* for "clarity" */
+	slave_dev  = dev_get (srq.slave_name);
 
-
-  master_dev = dev;		/* for "clarity" */
-  slave_dev  = dev_get (srq.slave_name);
-
-  if ( eql_is_slave (slave_dev) )	/* really is a slave */
-    {
-      equalizer_t *eql = (equalizer_t *) master_dev->priv;
-      slave_dev->flags = slave_dev->flags & ~IFF_SLAVE;
-
-      eql_remove_slave_dev (eql->queue, slave_dev);
-
-      return 0;
-    }
-  return -EINVAL;
+	if ( eql_is_slave (slave_dev) )	/* really is a slave */
+	{
+		equalizer_t *eql = (equalizer_t *) master_dev->priv;
+		slave_dev->flags = slave_dev->flags & ~IFF_SLAVE;
+		eql_remove_slave_dev (eql->queue, slave_dev);
+		return 0;
+	}
+	return -EINVAL;
 }
-
 
-static 
-int 
-eql_g_slave_cfg(struct device *dev, slave_config_t *scp)
-{
-  slave_t *slave;
-  equalizer_t *eql;
-  struct device *slave_dev;
-  slave_config_t sc;
-  int err;
 
-  err = verify_area(VERIFY_READ, (void *)scp, sizeof (slave_config_t));
-  if (err) return err;
+static int eql_g_slave_cfg(struct device *dev, slave_config_t *scp)
+{
+	slave_t *slave;
+	equalizer_t *eql;
+	struct device *slave_dev;
+	slave_config_t sc;
+	int err;
 
-  memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
+	err = verify_area(VERIFY_READ, (void *)scp, sizeof (slave_config_t));
+	if (err) 
+		return err;
 
+	memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
 #ifdef EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name);
+	if (eql_debug >= 20)
+		printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name);
 #endif
+	eql = (equalizer_t *) dev->priv;
+	slave_dev = dev_get (sc.slave_name);
 
-  eql = (equalizer_t *) dev->priv;
-  slave_dev = dev_get (sc.slave_name);
-
-  if ( eql_is_slave (slave_dev) )
-    {
-      slave = eql_find_slave_dev (eql->queue,  slave_dev);
-      if (slave != 0)
+	if ( eql_is_slave (slave_dev) )
 	{
-	  sc.priority = slave->priority;
-
-          err = verify_area(VERIFY_WRITE, (void *)scp, sizeof (slave_config_t));
-          if (err) return err;
-
-	  memcpy_tofs (scp, &sc, sizeof (slave_config_t));
-	  return 0;
+		slave = eql_find_slave_dev (eql->queue,  slave_dev);
+		if (slave != 0)
+		{
+			sc.priority = slave->priority;
+			err = verify_area(VERIFY_WRITE, (void *)scp, sizeof (slave_config_t));
+			if (err) 
+				return err;
+			memcpy_tofs (scp, &sc, sizeof (slave_config_t));
+			return 0;
+		}
 	}
-    }
-  return -EINVAL;
+	return -EINVAL;
 }
-
 
-static 
-int 
-eql_s_slave_cfg(struct device *dev, slave_config_t *scp)
-{
-  slave_t *slave;
-  equalizer_t *eql;
-  struct device *slave_dev;
-  slave_config_t sc;
-  int err;
 
-  err = verify_area(VERIFY_READ, (void *)scp, sizeof (slave_config_t));
-  if (err) return err;
+static int eql_s_slave_cfg(struct device *dev, slave_config_t *scp)
+{
+	slave_t *slave;
+	equalizer_t *eql;
+	struct device *slave_dev;
+	slave_config_t sc;
+	int err;
+
+	err = verify_area(VERIFY_READ, (void *)scp, sizeof (slave_config_t));
+	if (err) 
+		return err;
 
 #ifdef EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name);
+	if (eql_debug >= 20)
+		printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name);
 #endif
   
-  memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
+	memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
 
-  eql = (equalizer_t *) dev->priv;
-  slave_dev = dev_get (sc.slave_name);
+	eql = (equalizer_t *) dev->priv;
+	slave_dev = dev_get (sc.slave_name);
 
-  if ( eql_is_slave (slave_dev) )
-    {
-      slave = eql_find_slave_dev (eql->queue, slave_dev);
-      if (slave != 0)
-	{
-	  slave->priority = sc.priority;
-	  slave->priority_bps = sc.priority;
-	  slave->priority_Bps = sc.priority / 8;
-	  return 0;
+	if ( eql_is_slave (slave_dev) )
+	{
+		slave = eql_find_slave_dev (eql->queue, slave_dev);
+		if (slave != 0)
+		{
+			slave->priority = sc.priority;
+			slave->priority_bps = sc.priority;
+			slave->priority_Bps = sc.priority / 8;
+			return 0;
+		}
 	}
-    }
-  return -EINVAL;
+	return -EINVAL;
 }
-
 
-static 
-int 
-eql_g_master_cfg(struct device *dev, master_config_t *mcp)
+
+static int eql_g_master_cfg(struct device *dev, master_config_t *mcp)
 {
-  equalizer_t *eql;
-  master_config_t mc;
+	equalizer_t *eql;
+	master_config_t mc;
 
 #if EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: get master config\n", dev->name);
+	if (eql_debug >= 20)
+		printk ("%s: get master config\n", dev->name);
 #endif
 
-  if ( eql_is_master (dev) )
-    {
-      int err;
-
-      err = verify_area(VERIFY_WRITE, (void *)mcp, sizeof (master_config_t));
-      if (err) return err;
-
-      eql = (equalizer_t *) dev->priv;
-      mc.max_slaves = eql->max_slaves;
-      mc.min_slaves = eql->min_slaves;
-      memcpy_tofs (mcp, &mc, sizeof (master_config_t));
-      return 0;
-    }
-  return -EINVAL;
+	if ( eql_is_master (dev) )
+	{
+		int err;
+		err = verify_area(VERIFY_WRITE, (void *)mcp, sizeof (master_config_t));
+		if (err) 
+			return err;
+		eql = (equalizer_t *) dev->priv;
+		mc.max_slaves = eql->max_slaves;
+		mc.min_slaves = eql->min_slaves;
+		memcpy_tofs (mcp, &mc, sizeof (master_config_t));
+		return 0;
+	}
+	return -EINVAL;
 }
 
 
-static 
-int 
-eql_s_master_cfg(struct device *dev, master_config_t *mcp)
+static int eql_s_master_cfg(struct device *dev, master_config_t *mcp)
 {
-  equalizer_t *eql;
-  master_config_t mc;
- int err;
-
- err = verify_area(VERIFY_READ, (void *)mcp, sizeof (master_config_t));
- if (err) return err;
+	equalizer_t *eql;
+	master_config_t mc;
+	int err;
 
+	err = verify_area(VERIFY_READ, (void *)mcp, sizeof (master_config_t));
+	if (err)
+		return err;
 #if EQL_DEBUG
-  if (eql_debug >= 20)
-    printk ("%s: set master config\n", dev->name);
+	if (eql_debug >= 20)
+		printk ("%s: set master config\n", dev->name);
 #endif
-
-  memcpy_fromfs (&mc, mcp, sizeof (master_config_t));
-
-  if ( eql_is_master (dev) )
-    {
-      eql = (equalizer_t *) dev->priv;
-      eql->max_slaves = mc.max_slaves;
-      eql->min_slaves = mc.min_slaves;
-      return 0;
-    }
-  return -EINVAL;
+	memcpy_fromfs (&mc, mcp, sizeof (master_config_t));
+	if ( eql_is_master (dev) )
+	{
+		eql = (equalizer_t *) dev->priv;
+		eql->max_slaves = mc.max_slaves;
+		eql->min_slaves = mc.min_slaves;
+		return 0;
+	}
+	return -EINVAL;
 }
-
-/* private device support functions
-   ------------------------------------------------------------------
-   */
 
-static inline
-int 
-eql_is_slave(struct device *dev)
-{
-  if (dev)
-    {
-      if ((dev->flags & IFF_SLAVE) == IFF_SLAVE)
-	return 1;
-    }
-  return 0;
+/*
+ *	Private device support functions
+ */
+
+static inline int eql_is_slave(struct device *dev)
+{
+	if (dev)
+	{
+		if ((dev->flags & IFF_SLAVE) == IFF_SLAVE)
+			return 1;
+	}
+	return 0;
 }
 
 
-static inline
-int 
-eql_is_master(struct device *dev)
+static inline int eql_is_master(struct device *dev)
 {
-  if (dev)
-    {
-      if ((dev->flags & IFF_MASTER) == IFF_MASTER)
-	return 1;
-    }
-  return 0;
+	if (dev)
+	{
+		if ((dev->flags & IFF_MASTER) == IFF_MASTER)
+		return 1;
+	}
+	return 0;
 }
-
 
-static 
-slave_t *
-eql_new_slave(void)
+
+static slave_t *eql_new_slave(void)
 {
-  slave_t *slave;
+	slave_t *slave;
 
-  slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
-  if (slave)
-    {
-      memset(slave, 0, sizeof (slave_t));
-      return slave;
-    }
-  return 0;
+	slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
+	if (slave)
+	{
+		memset(slave, 0, sizeof (slave_t));
+		return slave;
+	}
+	return 0;
 }
 
 
-static 
-void
-eql_delete_slave(slave_t *slave)
+static void eql_delete_slave(slave_t *slave)
 {
-  kfree (slave);
+	kfree (slave);
 }
 
 
 #if 0				/* not currently used, will be used
 				   when we realy use a priority queue */
-static
-long
-slave_Bps(slave_t *slave)
+static long slave_Bps(slave_t *slave)
 {
-  return (slave->priority_Bps);
+	return (slave->priority_Bps);
 }
 
-static 
-long
-slave_bps(slave_t *slave)
+static long slave_bps(slave_t *slave)
 {
-  return (slave->priority_bps);
+	return (slave->priority_bps);
 }
+
 #endif
-
 
-static inline
-int 
-eql_number_slaves(slave_queue_t *queue)
+static inline int eql_number_slaves(slave_queue_t *queue)
 {
-  return queue->num_slaves;
+	return queue->num_slaves;
 }
 
-
-static inline 
-int 
-eql_is_empty(slave_queue_t *queue)
+static inline int eql_is_empty(slave_queue_t *queue)
 {
-  if (eql_number_slaves (queue) == 0)
-    return 1;
-  return 0;
+	if (eql_number_slaves (queue) == 0)
+		return 1;
+	return 0;
 }
 
-
-static inline
-int 
-eql_is_full(slave_queue_t *queue)
+static inline int eql_is_full(slave_queue_t *queue)
 {
-  equalizer_t *eql = (equalizer_t *) queue->master_dev->priv;
+	equalizer_t *eql = (equalizer_t *) queue->master_dev->priv;
 
-  if (eql_number_slaves (queue) == eql->max_slaves)
-    return 1;
-  return 0;
+	if (eql_number_slaves (queue) == eql->max_slaves)
+		return 1;
+	return 0;
 }
-
 
-static
-slave_queue_t *
-eql_new_slave_queue(struct device *dev)
-{
-  slave_queue_t *queue;
-  slave_t *head_slave;
-  slave_t *tail_slave;
-
-  queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
-  if (queue == NULL)
-    return 0;
-  memset (queue, 0, sizeof (slave_queue_t));
+static slave_queue_t *eql_new_slave_queue(struct device *dev)
+{
+	slave_queue_t *queue;
+	slave_t *head_slave;
+	slave_t *tail_slave;
+
+	queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
+	if (queue == NULL)
+		return 0;
+	memset (queue, 0, sizeof (slave_queue_t));
+	head_slave = eql_new_slave ();
+	tail_slave = eql_new_slave ();
 
-  head_slave = eql_new_slave ();
-  tail_slave = eql_new_slave ();
-  
-  if ( head_slave != 0 &&
-       tail_slave != 0 )
-    {
-      head_slave->next = tail_slave;
-      tail_slave->next = 0;
-      queue->head = head_slave;
-      queue->num_slaves = 0;
-      queue->master_dev = dev;
-    }
-  else
-    {
-      if (head_slave)
-         kfree(head_slave);
-      if (tail_slave)
-         kfree(tail_slave);
-      kfree (queue);
-      return 0;
-    }
-  return queue;
+	if ( head_slave != 0 &&
+		tail_slave != 0 )
+	{
+		head_slave->next = tail_slave;
+		tail_slave->next = 0;
+		queue->head = head_slave;
+		queue->num_slaves = 0;
+		queue->master_dev = dev;
+	}
+	else
+	{
+		if (head_slave)
+			kfree(head_slave);
+		if (tail_slave)
+			kfree(tail_slave);
+		kfree (queue);
+		return 0;
+	}
+	return queue;
 }
 
 
-static
-void
-eql_delete_slave_queue(slave_queue_t *queue)
+static void eql_delete_slave_queue(slave_queue_t *queue)
 { 
-  slave_t *zapped;
-
-  /* this should only be called when there isn't a timer running that scans
-     the data periodicaly.. dev_close stops the timer... */
+	slave_t *zapped;
+	/*
+	 *	This should only be called when there isn't a
+	 *	timer running that scans the data periodicaly.. 
+	 *	dev_close stops the timer... 
+	 */
 
-  while ( ! eql_is_empty (queue) )
-    {
-      zapped = eql_remove_slave (queue, queue->head->next);
-      eql_delete_slave (zapped);
-    }
-  kfree (queue->head->next);
-  kfree (queue->head);
-  kfree (queue);
+	while ( ! eql_is_empty (queue) )
+	{
+		zapped = eql_remove_slave (queue, queue->head->next);
+		eql_delete_slave (zapped);
+	}
+	kfree (queue->head->next);
+	kfree (queue->head);
+	kfree (queue);
 }
-
 
-static
-int
-eql_insert_slave(slave_queue_t *queue, slave_t *slave)
+static int eql_insert_slave(slave_queue_t *queue, slave_t *slave)
 {
-  cli ();
-
-  if ( ! eql_is_full (queue) )
-    {
-      slave_t *duplicate_slave = 0;
+	cli ();
 
-      duplicate_slave = eql_find_slave_dev (queue, slave->dev);
-
-      if (duplicate_slave != 0)
+	if ( ! eql_is_full (queue) )
 	{
-/*	  printk ("%s: found a duplicate, killing it and replacing\n",
-		  queue->master_dev->name); */
-	  eql_delete_slave (eql_remove_slave (queue, duplicate_slave));
+		slave_t *duplicate_slave = 0;
+		duplicate_slave = eql_find_slave_dev (queue, slave->dev);
+		if (duplicate_slave != 0)
+		{
+/*			  printk ("%s: found a duplicate, killing it and replacing\n",
+				  queue->master_dev->name); */
+			eql_delete_slave (eql_remove_slave (queue, duplicate_slave));
+		}
+		slave->next = queue->head->next;
+		queue->head->next = slave;
+		queue->num_slaves++;
+		sti ();
+		return 0;
 	}
-
-      slave->next = queue->head->next;
-      queue->head->next = slave;
-      queue->num_slaves++;
-      sti ();
-      return 0;
-    }
-
-  sti ();
-
-  return 1;
+	sti ();
+	return 1;
 }
 
 
-static
-slave_t *
-eql_remove_slave(slave_queue_t *queue, slave_t *slave)
+static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave)
 {
-  slave_t *prev;
-  slave_t *curr;
-
-  cli ();
-
-  prev = queue->head;
-  curr = queue->head->next;
-  while (curr != slave && 
-	 curr->dev != 0 )
-    {
-/* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */
-      prev = curr;
-      curr = curr->next;
-    }
+	slave_t *prev;
+	slave_t *curr;
 
-  if (curr == slave)
-    {
-      prev->next = curr->next;
-      queue->num_slaves--;
-      sti();
-      return curr;
-    }
+	cli ();
 
-  sti ();
+	prev = queue->head;
+	curr = queue->head->next;
+	while (curr != slave && 
+		curr->dev != 0 )
+	{
+/* 		printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */
+		prev = curr;
+		curr = curr->next;
+	}
 
-  return 0;			/* not found */
+	if (curr == slave)
+	{
+		prev->next = curr->next;
+		queue->num_slaves--;
+		sti();
+		return curr;
+	}
+	sti ();
+	return 0;			/* not found */
 }
-
+
 
 #if 0
-static 
-int 
-eql_insert_slave_dev(slave_queue_t *queue, struct device *dev)
-{
-  slave_t *slave;
-
-  cli ();
-
-  if ( ! eql_is_full (queue) )
-    {
-      slave = eql_new_slave ();
-      slave->dev = dev;
-      slave->priority = EQL_DEFAULT_SLAVE_PRIORITY;
-      slave->priority_bps = EQL_DEFAULT_SLAVE_PRIORITY;
-      slave->priority_Bps = EQL_DEFAULT_SLAVE_PRIORITY / 8;
-      slave->next = queue->head->next;
-      queue->head->next = slave;
-      sti ();
-      return 0;
-    }
-  sti ();
-  return 1;
+static int eql_insert_slave_dev(slave_queue_t *queue, struct device *dev)
+{
+	slave_t *slave;
+	cli ();
+
+	if ( ! eql_is_full (queue) )
+	{
+		slave = eql_new_slave ();
+		slave->dev = dev;
+		slave->priority = EQL_DEFAULT_SLAVE_PRIORITY;
+		slave->priority_bps = EQL_DEFAULT_SLAVE_PRIORITY;
+		slave->priority_Bps = EQL_DEFAULT_SLAVE_PRIORITY / 8;
+		slave->next = queue->head->next;
+		queue->head->next = slave;
+		sti ();
+		return 0;
+	}
+	sti ();
+	return 1;
 }
 #endif
 
 
-static
-int
-eql_remove_slave_dev(slave_queue_t *queue, struct device *dev)
+static int eql_remove_slave_dev(slave_queue_t *queue, struct device *dev)
 {
-  slave_t *prev;
-  slave_t *curr;
-  slave_t *target;
-
-  target = eql_find_slave_dev (queue, dev);
+	slave_t *prev;
+	slave_t *curr;
+	slave_t *target;
 
-  if (target != 0)
-    {
-      cli ();
+	target = eql_find_slave_dev (queue, dev);
 
-      prev = queue->head;
-      curr = prev->next;
-      while (curr != target)
+	if (target != 0)
 	{
-	  prev = curr;
-	  curr = curr->next;
+		cli ();
+		prev = queue->head;
+		curr = prev->next;
+		while (curr != target)
+		{
+			prev = curr;
+			curr = curr->next;
+		}
+		prev->next = curr->next;
+		queue->num_slaves--;
+		sti ();
+		eql_delete_slave (curr);
+		return 0;
 	}
-      prev->next = curr->next;
-      queue->num_slaves--;
-
-      sti ();
-
-      eql_delete_slave (curr);
-      return 0;
-    }
-  return 1;
+	return 1;
 }
-
 
-static inline
-struct device *
-eql_best_slave_dev(slave_queue_t *queue)
-{
-  if (queue->best_slave != 0)
-    {
-      if (queue->best_slave->dev != 0)
-	return queue->best_slave->dev;
-      else
-	return 0;
-    }
-  else
-    return 0;
+
+static inline struct device *eql_best_slave_dev(slave_queue_t *queue)
+{
+	if (queue->best_slave != 0)
+	{
+		if (queue->best_slave->dev != 0)
+			return queue->best_slave->dev;
+		else
+			return 0;
+	}
+	else
+		return 0;
 }
 
 
-static inline
-slave_t *
-eql_best_slave(slave_queue_t *queue)
+static inline slave_t *eql_best_slave(slave_queue_t *queue)
 {
-  return queue->best_slave;
+	return queue->best_slave;
 }
-
-static inline
-void
-eql_schedule_slaves(slave_queue_t *queue)
-{
-  struct device *master_dev = queue->master_dev;
-  slave_t *best_slave = 0;
-  slave_t *slave_corpse = 0;
+
+static inline void eql_schedule_slaves(slave_queue_t *queue)
+{
+	struct device *master_dev = queue->master_dev;
+	slave_t *best_slave = 0;
+	slave_t *slave_corpse = 0;
 
 #ifdef EQL_DEBUG
-  if (eql_debug >= 100)
-    printk ("%s: schedule %d slaves\n", 
-	    master_dev->name, eql_number_slaves (queue));
+	if (eql_debug >= 100)
+		printk ("%s: schedule %d slaves\n", 
+			master_dev->name, eql_number_slaves (queue));
 #endif
-
-  if ( eql_is_empty (queue) )
-    {
-      /* no slaves to play with */
-      eql_set_best_slave (queue, (slave_t *) 0);
-      return;
-    }
-  else
-    {				/* make a pass to set the best slave */
-      unsigned long best_load = (unsigned long) ULONG_MAX;
-      slave_t *slave = 0;
-      int i;
-
-      cli ();
-
-      for (i = 1, slave = eql_first_slave (queue);
-	   i <= eql_number_slaves (queue);
-	   i++, slave = eql_next_slave (queue, slave))
-	{
-	  /* go through the slave list once, updating best_slave 
-	     whenever a new best_load is found, whenever a dead
-	     slave is found, it is marked to be pulled out of the 
-	     queue */
-
-	  unsigned long slave_load;
-	  unsigned long bytes_queued; 
-	  unsigned long priority_Bps; 
-	  
-	  if (slave != 0)
-	    {
-	      bytes_queued = slave->bytes_queued;
-	      priority_Bps = slave->priority_Bps;    
-
-	      if ( slave->dev != 0)
+	if ( eql_is_empty (queue) )
+	{
+		/*
+		 *	No slaves to play with 
+		 */
+		eql_set_best_slave (queue, (slave_t *) 0);
+		return;
+	}
+	else
+	{		
+		/*
+		 *	Make a pass to set the best slave 
+		 */
+		unsigned long best_load = (unsigned long) ULONG_MAX;
+		slave_t *slave = 0;
+		int i;
+
+		cli ();
+		for (i = 1, slave = eql_first_slave (queue);
+			i <= eql_number_slaves (queue);
+			i++, slave = eql_next_slave (queue, slave))
 		{
-		  if ((slave->dev->flags & IFF_UP) == IFF_UP )
-		    {
-		      slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 
-			(priority_Bps) + bytes_queued * 8;
-		      
-		      if (slave_load < best_load)
+			/*
+			 *	Go through the slave list once, updating best_slave 
+			 *      whenever a new best_load is found, whenever a dead
+			 *	slave is found, it is marked to be pulled out of the 
+			 *	queue 
+			 */
+		
+			unsigned long slave_load;
+			unsigned long bytes_queued; 
+			unsigned long priority_Bps; 
+	  	
+	  		if (slave != 0)
 			{
-			  best_load = slave_load;
-			  best_slave = slave;
+				bytes_queued = slave->bytes_queued;
+				priority_Bps = slave->priority_Bps;    
+				if ( slave->dev != 0)
+				{
+					if ((slave->dev->flags & IFF_UP) == IFF_UP )
+					{
+						slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 
+							(priority_Bps) + bytes_queued * 8;
+		      				if (slave_load < best_load)
+						{
+							best_load = slave_load;
+							best_slave = slave;
+						}
+					}
+					else		/* we found a dead slave */
+					{
+						/* 
+						 *	We only bury one slave at a time, if more than
+						 *	one slave dies, we will bury him on the next 
+						 *	reschedule. slaves don't die all at once that 
+						 *	much anyway 
+						 */
+						slave_corpse = slave;
+					}
+				}
 			}
-		    }
-		  else		/* we found a dead slave */
-		    {
-		      /* we only bury one slave at a time, if more than
-			 one slave dies, we will bury him on the next 
-			 reschedule. slaves don't die all at once that much
-			 anyway */
-		      slave_corpse = slave;
-		    }
-		}
-	    }
-	} /* for */
-
-	   sti ();
-	   
-      eql_set_best_slave (queue, best_slave);
-    } /* else */
-
-  if (slave_corpse != 0)
-    {
-      printk ("eql: scheduler found dead slave, burying...\n");
-      eql_delete_slave (eql_remove_slave (queue, slave_corpse));
-    }
-
-  return;
+		} /* for */
+		sti ();
+		eql_set_best_slave (queue, best_slave);
+	} /* else */
+	if (slave_corpse != 0)
+	{
+		printk ("eql: scheduler found dead slave, burying...\n");
+		eql_delete_slave (eql_remove_slave (queue, slave_corpse));
+	}
+	return;
 }
-
 
-static
-slave_t *
-eql_find_slave_dev(slave_queue_t *queue, struct device *dev)
-{
-  slave_t *slave = 0;
 
-  slave = eql_first_slave(queue);
+static slave_t * eql_find_slave_dev(slave_queue_t *queue, struct device *dev)
+{
+	slave_t *slave = 0;
+	slave = eql_first_slave(queue);
 
-  while (slave != 0 && slave->dev != dev && slave != 0)
-    {
+	while (slave != 0 && slave->dev != dev && slave != 0)
+	{
 #if 0
-      if (slave->dev != 0)
-	printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name);
-      else
-	printk ("eql: find_slave_dev; looked at nothing...\n");
+		if (slave->dev != 0)
+			printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name);
+		else
+			printk ("eql: find_slave_dev; looked at nothing...\n");
 #endif
-
-      slave = slave->next;
-    }
-
-  return slave;
+		slave = slave->next;
+	}
+	return slave;
 }
 
 
-static inline
-slave_t *
-eql_first_slave(slave_queue_t *queue)
+static inline slave_t *eql_first_slave(slave_queue_t *queue)
 {
-  return queue->head->next;
+	return queue->head->next;
 }
 
 
-static inline
-slave_t *
-eql_next_slave(slave_queue_t *queue, slave_t *slave)
+static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave)
 {
-  return slave->next;
+	return slave->next;
 }
 
-
-static inline
-void
-eql_set_best_slave(slave_queue_t *queue, slave_t *slave)
+static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave)
 {
-  queue->best_slave = slave;
+	queue->best_slave = slave;
 }
-
 
 #if 0
-static inline
-int
-eql_lock_slave_queue(slave_queue_t *queue)
-{
-  int result = 0;
-
-  printk ("eql: lock == %d\n", queue->lock);
-  if (queue->lock)
-    {
-      printk ("eql: lock_slave-q sleeping for lock\n");
-      sleep_on (&eql_queue_lock);
-      printk ("eql: lock_slave-q woken up\n");
-      queue->lock = 1;
-    }
-  queue->lock = 1;
-  return result;
-}
-
-static inline
-int
-eql_unlock_slave_queue(slave_queue_t *queue)
-{
-  int result = 0;
-
-  if (queue->lock != 0)
-    {
-      queue->lock = 0;
-      printk ("eql: unlock_slave-q waking up lock waiters\n");
-      wake_up (&eql_queue_lock);
-    }
-  return result;
+static inline int eql_lock_slave_queue(slave_queue_t *queue)
+{
+	int result = 0;
+
+	printk ("eql: lock == %d\n", queue->lock);
+	if (queue->lock)
+	{
+		printk ("eql: lock_slave-q sleeping for lock\n");
+		sleep_on (&eql_queue_lock);
+		printk ("eql: lock_slave-q woken up\n");
+		queue->lock = 1;
+	}
+	queue->lock = 1;
+	return result;
 }
-#endif 
 
-static inline
-int
-eql_is_locked_slave_queue(slave_queue_t *queue)
+static inline int eql_unlock_slave_queue(slave_queue_t *queue)
 {
-  return test_bit(1, (void *) &queue->lock);
+	int result = 0;
+
+	if (queue->lock != 0)
+	{
+		queue->lock = 0;
+		printk ("eql: unlock_slave-q waking up lock waiters\n");
+		wake_up (&eql_queue_lock);
+	}
+	return result;
 }
+#endif 
 
-static 
-void
-eql_timer(unsigned long param)
+static inline int eql_is_locked_slave_queue(slave_queue_t *queue)
 {
-  equalizer_t *eql = (equalizer_t *) param;
-  slave_t *slave;
-  slave_t *slave_corpse = 0;
-  int i;
-
-  if ( ! eql_is_empty (eql->queue) )
-    {
-      cli ();
+	return test_bit(1, (void *) &queue->lock);
+}
 
-      for (i = 1, slave = eql_first_slave (eql->queue);
-	   i <= eql_number_slaves (eql->queue);
-	   i++, slave = eql_next_slave (eql->queue, slave))
+static void eql_timer(unsigned long param)
+{
+	equalizer_t *eql = (equalizer_t *) param;
+	slave_t *slave;
+	slave_t *slave_corpse = 0;
+	int i;
+	
+	if ( ! eql_is_empty (eql->queue) )
 	{
-	  if (slave != 0)
-	    {
-	      if ((slave->dev->flags & IFF_UP) == IFF_UP )
+		cli ();
+		for (i = 1, slave = eql_first_slave (eql->queue);
+			i <= eql_number_slaves (eql->queue);
+			i++, slave = eql_next_slave (eql->queue, slave))
 		{
-		  slave->bytes_queued -= slave->priority_Bps;
-	      
-		  if (slave->bytes_queued < 0)
-		    slave->bytes_queued = 0;
+			if (slave != 0)
+			{
+				if ((slave->dev->flags & IFF_UP) == IFF_UP )
+				{
+					slave->bytes_queued -= slave->priority_Bps;
+					if (slave->bytes_queued < 0)
+						slave->bytes_queued = 0;
+				}
+				else
+					slave_corpse = slave;
+			}
 		}
-	      else
+		sti ();
+		if (slave_corpse != 0)
 		{
-		  slave_corpse = slave;
+			printk ("eql: timer found dead slave, burying...\n");
+			eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse));
 		}
-	    }
 	}
 
-      sti ();
-      
-      if (slave_corpse != 0)
+	if (eql->timer_on != 0) 
 	{
-	  printk ("eql: timer found dead slave, burying...\n");
-	  eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse));
+		eql->timer.expires = jiffies+EQL_DEFAULT_RESCHED_IVAL;
+		add_timer (&eql->timer);
 	}
-
-    }
-
-  if (eql->timer_on != 0) 
-    {
-      eql->timer.expires = jiffies+EQL_DEFAULT_RESCHED_IVAL;
-      add_timer (&eql->timer);
-    }
 }
 
 #ifdef MODULE
-static struct device dev_eql = {
-	"eql", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eql_init };
+static struct device dev_eql = 
+{
+	"eql", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eql_init 
+};
 
 int init_module(void)
 {
@@ -1196,8 +1089,7 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	unregister_netdev(&dev_eql);
 }
diff -u --recursive --new-file v1.3.59/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v1.3.59/linux/drivers/net/loopback.c	Sun Oct 29 11:38:49 1995
+++ linux/drivers/net/loopback.c	Wed Feb  7 08:55:36 1996
@@ -141,7 +141,8 @@
 	dev->hard_header	= eth_header;
 	dev->hard_header_len	= ETH_HLEN;		/* 14			*/
 	dev->addr_len		= ETH_ALEN;		/* 6			*/
-	dev->type		= ARPHRD_ETHER;		/* 0x0001		*/
+	dev->tx_queue_len	= 50000;		/* No limit on loopback */
+	dev->type		= ARPHRD_LOOPBACK;	/* 0x0001		*/
 	dev->rebuild_header	= eth_rebuild_header;
 	dev->open		= loopback_open;
 	dev->flags		= IFF_LOOPBACK|IFF_BROADCAST;
diff -u --recursive --new-file v1.3.59/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.3.59/linux/drivers/net/net_init.c	Sun Jan 14 16:30:11 1996
+++ linux/drivers/net/net_init.c	Wed Feb  7 08:55:36 1996
@@ -166,18 +166,18 @@
 	}
 
 	dev->hard_header	= eth_header;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->set_mac_address = eth_mac_addr;
-	dev->header_cache_bind = eth_header_cache_bind;
-	dev->header_cache_update = eth_header_cache_update;
+	dev->rebuild_header 	= eth_rebuild_header;
+	dev->set_mac_address 	= eth_mac_addr;
+	dev->header_cache_bind 	= eth_header_cache_bind;
+	dev->header_cache_update= eth_header_cache_update;
 
 	dev->type		= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
+	dev->hard_header_len 	= ETH_HLEN;
 	dev->mtu		= 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
+	dev->addr_len		= ETH_ALEN;
+	dev->tx_queue_len	= 100;	/* Ethernet wants good queues */	
+	
+	memset(dev->broadcast,0xFF, ETH_ALEN);
 
 	/* New-style flags. */
 	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
@@ -199,15 +199,15 @@
 		skb_queue_head_init(&dev->buffs[i]);
 
 	dev->hard_header	= tr_header;
-	dev->rebuild_header = tr_rebuild_header;
+	dev->rebuild_header 	= tr_rebuild_header;
 
 	dev->type		= ARPHRD_IEEE802;
-	dev->hard_header_len = TR_HLEN;
+	dev->hard_header_len 	= TR_HLEN;
 	dev->mtu		= 2000; /* bug in fragmenter...*/
-	dev->addr_len	= TR_ALEN;
-	for (i = 0; i < TR_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
+	dev->addr_len		= TR_ALEN;
+	dev->tx_queue_len	= 100;	/* Long queues on tr */
+	
+	memset(dev->broadcast,0xFF, TR_ALEN);
 
 	/* New-style flags. */
 	dev->flags		= IFF_BROADCAST;
@@ -306,18 +306,18 @@
 	 */
 #ifdef CONFIG_NET_ALIAS		
 	if (dev_base == dev)
-	  dev_base = net_alias_nextdev(dev);
+		dev_base = net_alias_nextdev(dev);
 	else
 	{
-	    while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */
-		    d = net_alias_nextdev(d);
+		while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */
+			d = net_alias_nextdev(d);
 	  
-	    if (d && (net_alias_nextdev(d) == dev))
+		if (d && (net_alias_nextdev(d) == dev))
 		{
-    /*
-	 * 	critical: bypass by consider devices as blocks (maindev+aliases)
-	 */
-		    net_alias_nextdev_set(d, net_alias_nextdev(dev)); 
+			/*
+			 * 	Critical: Bypass by consider devices as blocks (maindev+aliases)
+			 */
+			net_alias_nextdev_set(d, net_alias_nextdev(dev)); 
 		}
 #else
 	if (dev_base == dev)
@@ -326,7 +326,7 @@
 	{
 		while (d && (d->next != dev))
 			d = d->next;
-			
+		
 		if (d && (d->next == dev)) 
 		{
 			d->next = dev->next;
@@ -350,13 +350,15 @@
 
 	restore_flags(flags);
 
-	/* You can i.e use a interfaces in a route though it is not up.
-	   We call close_dev (which is changed: it will down a device even if
-	   dev->flags==0 (but it will not call dev->stop if IFF_UP
-	   is not set).
-	   This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev),
-	   dev_mc_discard(dev), ....
-	*/
+	/*
+	 *	You can i.e use a interfaces in a route though it is not up.
+	 *	We call close_dev (which is changed: it will down a device even if
+	 *	dev->flags==0 (but it will not call dev->stop if IFF_UP
+	 *	is not set).
+	 *	This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev),
+	 *	dev_mc_discard(dev), ....
+	 */
+	 
 	dev_close(dev);
 }
 
diff -u --recursive --new-file v1.3.59/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.59/linux/drivers/net/ppp.c	Mon Dec 11 15:42:03 1995
+++ linux/drivers/net/ppp.c	Wed Feb  7 08:55:37 1996
@@ -126,8 +126,8 @@
 #define PPP_LQR 0xc025  /* Link Quality Reporting Protocol */
 #endif
 
-static int ppp_register_compressor (struct compressor *cp);
-static void ppp_unregister_compressor (struct compressor *cp);
+int ppp_register_compressor (struct compressor *cp);
+void ppp_unregister_compressor (struct compressor *cp);
 
 /*
  * Local functions
@@ -480,7 +480,8 @@
 	dev->do_ioctl         = ppp_dev_ioctl;
 	dev->addr_len         = 0;
 	dev->type             = ARPHRD_PPP;
-
+	dev->tx_queue_len     = 10;
+	
 	for (indx = 0; indx < DEV_NUMBUFFS; indx++)
 		skb_queue_head_init (&dev->buffs[indx]);
 
@@ -3465,7 +3466,7 @@
 	return (struct compressor *) 0;
 }
 
-static int ppp_register_compressor (struct compressor *cp)
+int ppp_register_compressor (struct compressor *cp)
 {
 	struct compressor_link *new;
 	unsigned long flags;
@@ -3492,7 +3493,7 @@
 	return 0;
 }
 
-static void ppp_unregister_compressor (struct compressor *cp)
+void ppp_unregister_compressor (struct compressor *cp)
 {
 	struct compressor_link *prev = (struct compressor_link *) 0;
 	struct compressor_link *lnk;
diff -u --recursive --new-file v1.3.59/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.3.59/linux/drivers/net/slhc.c	Tue Jan 23 21:15:42 1996
+++ linux/drivers/net/slhc.c	Wed Feb  7 08:55:37 1996
@@ -91,7 +91,6 @@
 static unsigned char * put16(unsigned char *cp, unsigned short x);
 static unsigned short pull16(unsigned char **cpp);
 static void export_slhc_syms(void);
-static has_exported = 0;
 
 /* Initialize compression data structure
  *	slots must be in range 0 to 255 (zero meaning no compression)
@@ -103,9 +102,6 @@
 	register struct cstate *ts;
 	struct slcompress *comp;
 
-	if (!has_exported)
-		export_slhc_syms();
-
 	comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
 					    GFP_KERNEL);
 	if (! comp)
@@ -748,7 +744,6 @@
 static void export_slhc_syms(void)
 {
 	register_symtab(&slhc_syms);
-	has_exported = 1;
 }
 
 #ifdef MODULE
@@ -764,5 +759,11 @@
 {
 	return;
 }
-#endif /* MODULE */
+#else /* MODULE */
+
+void slhc_install(void)
+{
+	export_slhc_syms();
+}
+#endif
 #endif /* CONFIG_INET */
diff -u --recursive --new-file v1.3.59/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.59/linux/drivers/net/slip.c	Sat Nov 25 19:04:47 1995
+++ linux/drivers/net/slip.c	Wed Feb  7 08:55:37 1996
@@ -1022,7 +1022,7 @@
 	 case SIOCGIFNAME:
 		err = verify_area(VERIFY_WRITE, arg, strlen(sl->dev->name) + 1);
 		if (err)  {
-			return -err;
+			return err;
 		}
 		memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
 		return 0;
@@ -1030,7 +1030,7 @@
 	case SIOCGIFENCAP:
 		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
 		if (err)  {
-			return -err;
+			return err;
 		}
 		put_user(sl->mode, (int *)arg);
 		return 0;
@@ -1038,7 +1038,7 @@
 	case SIOCSIFENCAP:
 		err = verify_area(VERIFY_READ, arg, sizeof(int));
 		if (err)  {
-			return -err;
+			return err;
 		}
 		tmp = get_user((int *)arg);
 #ifndef SL_INCLUDE_CSLIP
@@ -1205,6 +1205,7 @@
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
+	dev->tx_queue_len	= 10;
 #ifdef CONFIG_AX25
 	if (sl->dev->type == 260) {
 		sl->dev->type = ARPHRD_AX25;
diff -u --recursive --new-file v1.3.59/linux/drivers/net/tunnel.c linux/drivers/net/tunnel.c
--- v1.3.59/linux/drivers/net/tunnel.c	Fri Nov 17 08:42:26 1995
+++ linux/drivers/net/tunnel.c	Wed Feb  7 08:55:37 1996
@@ -82,10 +82,11 @@
 	dev->stop = &tunnel_close;
 #endif
 	/* Now stomp the bits that are different */
-	dev->type = ARPHRD_TUNNEL; /* IP tunnel hardware type (Linux 1.1.89) */
+	dev->type = ARPHRD_TUNNEL; 	/* IP tunnel hardware type (Linux 1.1.89) */
 	dev->flags |= IFF_NOARP;
-	dev->flags |= IFF_LOOPBACK; /* Why doesn't tunnel work without this? [ should do now - AC]*/
+	dev->flags |= IFF_LOOPBACK; 	/* Why doesn't tunnel work without this? [ should do now - AC]*/
 	dev->addr_len=0;
+	dev->tx_queue_len=2;		/* Small queue - it should all run through */
 	dev->hard_header_len=0;
 	dev->hard_header=NULL;
 	dev->header_cache_bind=NULL;
diff -u --recursive --new-file v1.3.59/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v1.3.59/linux/drivers/pci/pci.c	Tue Jan 23 21:15:42 1996
+++ linux/drivers/pci/pci.c	Wed Feb  7 08:55:37 1996
@@ -70,6 +70,7 @@
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5434_8,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5436,	"GD 5436"),
+	DEVICE( CIRRUS,		CIRRUS_6205,	"GD 6205"),
 	DEVICE( CIRRUS,		CIRRUS_6729,	"CL 6729"),
 	DEVICE( CIRRUS,		CIRRUS_7542,	"CL 7542"),
 	DEVICE( CIRRUS,		CIRRUS_7543,	"CL 7543"),
@@ -105,6 +106,7 @@
 	DEVICE( BUSLOGIC,	BUSLOGIC_930,	"BT-930"),
 	DEVICE( PROMISE,	PROMISE_5300,	"DC5030"),
 	DEVICE( N9,		N9_I128,	"Imagine 128"),
+	DEVICE( N9,		N9_I128_2,	"Imagine 128v2"),
 	DEVICE( UMC,		UMC_UM8673F,	"UM8673F"),
 	BRIDGE( UMC,		UMC_UM8891A,	"UM8891A", 		0x01),
 	DEVICE( UMC,		UMC_UM8886BF,	"UM8886BF"),
@@ -138,14 +140,18 @@
 	DEVICE( ASP,		ASP_ABP940,	"ABP940"),
 	DEVICE( IMS,		IMS_8849,	"8849"),
 	DEVICE( TEKRAM2,	TEKRAM2_690c,	"DC690c"),
+	DEVICE( INTERG,		INTERG_1680,	"IGA-1680"),
 	DEVICE( REALTEK,	REALTEK_8029,	"8029"),
+	DEVICE( INIT,		INIT_320P,	"320 P"),
 	DEVICE( VIA,		VIA_82C505,	"VT 82C505"),
 	DEVICE( VIA,		VIA_82C561,	"VT 82C561"),
 	DEVICE( VIA,		VIA_82C576,	"VT 82C576 3V"),
+	DEVICE( VIA,		VIA_82C416,	"VT 82C416MV"),
 	DEVICE( VORTEX,		VORTEX_GDT,	"GDT 6000b"),
 	DEVICE( EF,		EF_ATM_FPGA,		"155P-MF1 (FPGA)"),
 	DEVICE( EF,		EF_ATM_ASIC,	"155P-MF1 (ASIC)"),
 	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
+	DEVICE( FORE,		FORE_PCA200PC, "PCA-200PC"),
 	DEVICE( PLX,		PLX_9060,	"PCI9060 i960 bridge"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROMOTIO, "Promotion-6410"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROVIDEO, "Provideo"),
@@ -217,7 +223,7 @@
 	const char	*off;
 	const char	*on;
 } bridge_optimization[] = {
-	{"Cache L2",			"write trough",	"write back"},
+	{"Cache L2",			"write through",	"write back"},
 	{"CPU-PCI posted write",	"off",		"on"},
 	{"CPU-Memory posted write",	"off",		"on"},
 	{"PCI-Memory posted write",	"off",		"on"},
@@ -409,11 +415,13 @@
 	      case PCI_VENDOR_ID_PCTECH:	return "PCTECH";
 	      case PCI_VENDOR_ID_DPT:		return "DPT";
 	      case PCI_VENDOR_ID_OPTI:		return "OPTI";
+	      case PCI_VENDOR_ID_SGS:		return "SGS Thomson";
 	      case PCI_VENDOR_ID_BUSLOGIC:	return "BusLogic";
 	      case PCI_VENDOR_ID_PROMISE:	return "Promise Technology";
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
 	      case PCI_VENDOR_ID_X:		return "X TECHNOLOGY";
+	      case PCI_VENDOR_ID_NEXGEN:	return "Nexgen";
 	      case PCI_VENDOR_ID_QLOGIC:	return "Q Logic";
 	      case PCI_VENDOR_ID_LEADTEK:	return "Leadtek Research";
 	      case PCI_VENDOR_ID_CONTAQ:	return "Contaq";
@@ -430,10 +438,13 @@
 	      case PCI_VENDOR_ID_IMS:		return "IMS";
 	      case PCI_VENDOR_ID_TEKRAM2:	return "Tekram";
 	      case PCI_VENDOR_ID_AMCC:		return "AMCC";
+	      case PCI_VENDOR_ID_INTERG:	return "Intergraphics";
 	      case PCI_VENDOR_ID_REALTEK:	return "Realtek";
+	      case PCI_VENDOR_ID_INIT:		return "Initio Corp";
 	      case PCI_VENDOR_ID_VIA:		return "VIA Technologies";
 	      case PCI_VENDOR_ID_VORTEX:	return "VORTEX";
 	      case PCI_VENDOR_ID_EF:		return "Efficient Networks";
+	      case PCI_VENDOR_ID_FORE:		return "Fore Systems";
 	      case PCI_VENDOR_ID_IMAGINGTECH:	return "Imaging Technology";
 	      case PCI_VENDOR_ID_PLX:		return "PLX";
 	      case PCI_VENDOR_ID_ALLIANCE:	return "Alliance";
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.59/linux/drivers/scsi/Config.in	Fri Jan 26 01:37:06 1996
+++ linux/drivers/scsi/Config.in	Thu Jan 25 21:01:33 1996
@@ -19,12 +19,18 @@
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
-dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG.SCSI
+dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
 dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
 dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
-dep_tristate 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
+dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
+if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
+	bool '   Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
+	choice 'NCR5380/53c400 mapping method (use Port for T130B)' \
+		"Port CONFIG_SCSI_G_NCR5380_PORT \
+		Memory CONFIG_SCSI_G_NCR5380_MEM" Port
+fi
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
 fi
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v1.3.59/linux/drivers/scsi/Makefile	Fri Jan 26 01:37:06 1996
+++ linux/drivers/scsi/Makefile	Thu Jan 25 20:54:42 1996
@@ -20,6 +20,9 @@
 
 AHA152X	= -DDEBUG_AHA152X -DAUTOCONF
 
+.SUFFIXES:
+.SUFFIXES: .c .o .h .a
+
 ifeq (${CFLAGS},)
 CFLAGS = -D__KERNEL__=1 \
 	-DMODULE -Wall -Wstrict-prototypes -I. -I../../include \
@@ -183,8 +186,10 @@
 
 ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),y)
 L_OBJS += g_NCR5380.o
+EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
 else
   ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),m)
+  EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
   M_OBJS += g_NCR5380.o
   endif
 endif
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v1.3.59/linux/drivers/scsi/NCR5380.c	Wed Aug  2 13:21:06 1995
+++ linux/drivers/scsi/NCR5380.c	Thu Jan 25 20:54:42 1996
@@ -1,4 +1,6 @@
-#define NDEBUG (NDEBUG_RESTART_SELECT)
+#ifndef NDEBUG
+#define NDEBUG (NDEBUG_RESTART_SELECT | NDEBUG_ABORT)
+#endif
 /* 
  * NCR 5380 generic driver routines.  These should make it *trivial*
  * 	to implement 5380 SCSI drivers under Linux with a non-trantor
@@ -66,6 +68,14 @@
  *      the high level code.
  */
 
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) {printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) {printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
 #ifndef notyet
 #undef LINKED
 #undef USLEEP
@@ -284,6 +294,8 @@
  * code finishes, NCR5380_print_options should be called.
  */
 
+static int do_abort (struct Scsi_Host *host);
+static void do_reset (struct Scsi_Host *host);
 static struct Scsi_Host *first_instance = NULL;
 static Scsi_Host_Template *the_template = NULL;
 
@@ -354,18 +366,22 @@
     icr = NCR5380_read(INITIATOR_COMMAND_REG);
     basr = NCR5380_read(BUS_AND_STATUS_REG);
     sti();
+    printk("STATUS_REG: %02x ", status);
     for (i = 0; signals[i].mask ; ++i) 
 	if (status & signals[i].mask)
-	    printk(" %s", signals[i].name);
+	    printk(",%s", signals[i].name);
+    printk("\nBASR: %02x ", basr);
     for (i = 0; basrs[i].mask ; ++i) 
 	if (basr & basrs[i].mask)
-	    printk(" %s", basrs[i].name);
+	    printk(",%s", basrs[i].name);
+    printk("\nICR: %02x ", icr);
     for (i = 0; icrs[i].mask; ++i) 
 	if (icr & icrs[i].mask)
-	    printk(" %s", icrs[i].name);
+	    printk(",%s", icrs[i].name);
+    printk("\nMODE: %02x ", mr);
     for (i = 0; mrs[i].mask; ++i) 
 	if (mr & mrs[i].mask)
-	    printk(" %s", mrs[i].name);
+	    printk(",%s", mrs[i].name);
     printk("\n");
 }
 
@@ -693,11 +709,9 @@
     printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
 #endif
     printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
-#ifdef NCR53C400
-    if (hostdata->flags & FLAG_NCR53C400) {
+    if (((struct NCR5380_hostdata *)instance->hostdata)->flags & FLAG_NCR53C400) {
 	printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
     }
-#endif
 }
 
 /*
@@ -762,7 +776,8 @@
 
 static void NCR5380_init (struct Scsi_Host *instance, int flags) {
     NCR5380_local_declare();
-    int i;
+    int i, pass;
+    unsigned long timeout;
     struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
 	instance->hostdata;
 
@@ -772,7 +787,7 @@
      */
 
     if (flags & FLAG_NCR53C400)
-	instance->io_port += 8;
+	instance->NCR5380_instance_name += NCR53C400_address_adjust;
 
     NCR5380_setup(instance);
 
@@ -792,7 +807,12 @@
     hostdata->connected = NULL;
     hostdata->issue_queue = NULL;
     hostdata->disconnected_queue = NULL;
-    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
+
+    /* The CHECK code seems to break the 53C400. Will check it later maybe */
+    if (flags & FLAG_NCR53C400)
+	hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
+    else
+	hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
 
     if (!the_template) {
 	the_template = instance->hostt;
@@ -816,11 +836,49 @@
     NCR5380_write(MODE_REG, MR_BASE);
     NCR5380_write(TARGET_COMMAND_REG, 0);
     NCR5380_write(SELECT_ENABLE_REG, 0);
-#ifdef NCR53C400
+
     if (hostdata->flags & FLAG_NCR53C400) {
 	NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
     }
-#endif
+
+    /* 
+     * Detect and correct bus wedge problems.
+     *
+     * If the system crashed, it may have crashed in a state 
+     * where a SCSI command was still executing, and the 
+     * SCSI bus is not in a BUS FREE STATE.
+     *
+     * If this is the case, we'll try to abort the currently
+     * established nexus which we know nothing about, and that
+     * failing, do a hard reset of the SCSI bus 
+     */
+
+    for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) &&
+	  pass <= 6 ; ++pass) {
+	switch (pass) {
+	case 1:
+	case 3:
+	case 5:
+	    printk("scsi%d: SCSI bus busy, waiting up to five seconds\n",
+		instance->host_no);
+	    timeout = jiffies + 500;
+	    while (jiffies < timeout && (NCR5380_read(STATUS_REG) & SR_BSY));
+	    break;
+	case 2:
+	    printk("scsi%d: bus busy, attempting abort\n",
+		instance->host_no);
+	    do_abort (instance);
+	    break;
+	case 4:
+	    printk("scsi%d: bus busy, attempting reset\n",
+		instance->host_no);
+	    do_reset (instance);
+	    break;
+	case 6:
+	    printk("scsi%d: bus locked solid or invalid override\n",
+		instance->host_no);
+	}
+    }
 }
 
 /* 
@@ -846,8 +904,9 @@
 static
 #endif
 int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
+    struct Scsi_Host *instance = cmd->host;
     struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
-	cmd->host->hostdata;
+	instance->hostdata;
     Scsi_Cmnd *tmp;
 
 #if (NDEBUG & NDEBUG_NO_WRITE)
@@ -883,11 +942,13 @@
 
     cli();
     if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+	LIST(cmd, hostdata->issue_queue);
 	cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
 	hostdata->issue_queue = cmd;
     } else {
 	for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; 
 		tmp = (Scsi_Cmnd *) tmp->host_scribble);
+	LIST(cmd, tmp);
 	tmp->host_scribble = (unsigned char *) cmd;
     }
 #if (NDEBUG & NDEBUG_QUEUES)
@@ -945,16 +1006,29 @@
 		 * Search through the issue_queue for a command destined
 		 * for a target that's not busy.
 		 */
+#if (NDEBUG & NDEBUG_LISTS)
+		for (tmp= (Scsi_Cmnd *) hostdata->issue_queue, prev=NULL; tmp && (tmp != prev); prev=tmp, tmp=(Scsi_Cmnd*)tmp->host_scribble)
+		    ;
+		    /*printk("%p  ", tmp);*/
+		if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
 		for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, 
 		    prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) 
-		    tmp->host_scribble) 
+		    tmp->host_scribble) {
 
+#if (NDEBUG & NDEBUG_LISTS)
+		    if (prev != tmp)
+			printk("MAIN tmp=%p   target=%d   busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun);
+#endif
 		    /*  When we find one, remove it from the issue queue. */
 		    if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
-			    if (prev)
-				prev->host_scribble = tmp->host_scribble;
-			    else
-				hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+			if (prev) {
+			    REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
+			    prev->host_scribble = tmp->host_scribble;
+			} else {
+			    REMOVE(-1,hostdata->issue_queue,tmp,tmp->host_scribble);
+			    hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+			}
 			tmp->host_scribble = NULL;
 
 			/* reenable interrupts after finding one */
@@ -970,22 +1044,35 @@
 			printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n",
 			    instance->host_no, tmp->target, tmp->lun);
 #endif
+    		
+			/*
+			 * A successful selection is defined as one that 
+			 * leaves us with the command connected and 
+			 * in hostdata->connected, OR has terminated the
+			 * command.
+			 *
+			 * With successfull commands, we fall through
+			 * and see if we can do an information transfer,
+			 * with failures we will restart.
+			 */
+
+			if (!NCR5380_select(instance, tmp, 
     			/* 
     			 * REQUEST SENSE commands are issued without tagged
     			 * queueing, even on SCSI-II devices because the 
     			 * contingent allegiance condition exists for the 
     			 * entire unit.
     			 */
-    			
-			if (!NCR5380_select(instance, tmp, 
 			    (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : 
 			    TAG_NEXT)) {
 			    break;
 			} else {
 			    cli();
+			    LIST(tmp, hostdata->issue_queue);
 			    tmp->host_scribble = (unsigned char *) 
 				hostdata->issue_queue;
 			    hostdata->issue_queue = tmp;
+			    done = 0;
 			    sti();
 #if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
 			printk("scsi%d : main(): select() failed, returned to issue_queue\n",
@@ -993,6 +1080,7 @@
 #endif
 			}
 		    } /* if target/lun is not busy */
+		} /* for */
 	    } /* if (!hostdata->connected) */
 		
 	    if (hostdata->connected 
@@ -1108,7 +1196,8 @@
 				     && jiffies < timeout)
 				;
 			      if (jiffies >= timeout)
-				printk("scsi: timeout at %d\n", __LINE__);
+			        printk("scsi%d: timeout at NCR5380.c:%d\n", 
+				    host->host_no, __LINE__);
 			    }
 #else /* NCR_TIMEOUT */
 			    while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
@@ -1294,6 +1383,12 @@
      */
     NCR5380_write(SELECT_ENABLE_REG, 0);
 
+    /*
+     * The initiator shall then wait at least two deskew delays and release 
+     * the BSY signal.
+     */
+    udelay(1);        /* wingel -- wait two bus deskew delay >2*45ns */
+
     /* Reset BSY */
     NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | 
 	ICR_ASSERT_ATN | ICR_ASSERT_SEL));
@@ -1310,6 +1405,9 @@
      * the 1 us delay is arbitrary, and only used because this delay will 
      * be the same on other platforms and since it works here, it should 
      * work there.
+     *
+     * wingel suggests that this could be due to failing to wait
+     * one deskew delay.
      */
 
     udelay(1);
@@ -1344,6 +1442,14 @@
 	    return -1;
     }
 
+    /* 
+     * No less than two deskew delays after the initiator detects the 
+     * BSY signal is true, it shall release the SEL signal and may 
+     * change the DATA BUS.                                     -wingel
+     */
+
+    udelay(1);
+
     NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
 
     if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
@@ -1387,7 +1493,21 @@
      */
 
     /* Wait for start of REQ/ACK handshake */
+#ifdef NCR_TIMEOUT
+    {
+      unsigned long timeout = jiffies + NCR_TIMEOUT;
+ 
+      while (!(NCR5380_read(STATUS_REG) & SR_REQ) && jiffies < timeout);
+
+      if (jiffies >= timeout) {
+        printk("scsi%d: timeout at NCR5380.c:%d\n", __LINE__);
+        NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+        return -1;
+      }
+    }
+#else /* NCR_TIMEOUT */
     while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+#endif /* def NCR_TIMEOUT */
 
 #if (NDEBUG & NDEBUG_SELECTION)
     printk("scsi%d : target %d selected, going into MESSAGE OUT phase.\n",
@@ -1470,6 +1590,13 @@
     register unsigned char *d = *data;
     NCR5380_setup(instance);
 
+#if (NDEBUG & NDEBUG_PIO)
+    if (!(p & SR_IO))
+      printk("scsi%d : pio write %d bytes\n", instance->host_no, c);
+    else
+      printk("scsi%d : pio read %d bytes\n", instance->host_no, c);
+#endif
+
     /* 
      * The NCR5380 chip will only drive the SCSI bus when the 
      * phase specified in the appropriate bits of the TARGET COMMAND
@@ -1581,6 +1708,73 @@
 	return -1;
 }
 
+static void do_reset (struct Scsi_Host *host) {
+    NCR5380_local_declare();
+    NCR5380_setup(host);
+
+    cli();
+    NCR5380_write(TARGET_COMMAND_REG, 
+	PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+    udelay(25);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+    sti();
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ * 
+ * Purpose : abort the currently established nexus.  Should only be 
+ * 	called from a routine which can drop into a 
+ * 
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host) {
+    NCR5380_local_declare();
+    unsigned char tmp, *msgptr, phase;
+    int len;
+    NCR5380_setup(host);
+
+
+    /* Request message out phase */
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+    /* 
+     * Wait for the target to indicate a valid phase by asserting 
+     * REQ.  Once this happens, we'll have either a MSGOUT phase 
+     * and can immediately send the ABORT message, or we'll have some 
+     * other phase and will have to source/sink data.
+     * 
+     * We really don't care what value was on the bus or what value
+     * the target see's, so we just handshake.
+     */
+    
+    while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+    if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
+	    ICR_ASSERT_ACK);
+	while (NCR5380_read(STATUS_REG) & SR_REQ);
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+    }
+   
+    tmp = ABORT;
+    msgptr = &tmp;
+    len = 1;
+    phase = PHASE_MSGOUT;
+    NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+    /*
+     * If we got here, and the command completed successfully,
+     * we're about to go into bus free state.
+     */
+
+    return len ? -1 : 0;
+}
+
 #if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
 /* 
  * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, 
@@ -1656,7 +1850,13 @@
 #if defined(PSEUDO_DMA) && !defined(UNSAFE)
     cli();
 #endif
-    NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+    /* KLL May need eop and parity in 53c400 */
+    if (hostdata->flags & FLAG_NCR53C400)
+	NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK
+	| MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE
+	| MR_MONITOR_BSY);
+    else
+	NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
 #endif /* def REAL_DMA */
 
 #if (NDEBUG & NDEBUG_DMA) & 0
@@ -1798,7 +1998,12 @@
     return 0;
 #else /* defined(REAL_DMA_POLL) */
     if (p & SR_IO) {
-	if (!(foo = NCR5380_pread(instance, d, c - 1))) {
+	int diff = 1;
+	if (hostdata->flags & FLAG_NCR53C400) {
+	    diff=0;
+	}
+
+	if (!(foo = NCR5380_pread(instance, d, c - diff))) {
 	    /*
 	     * We can't disable DMA mode after successfully transferring 
 	     * what we plan to be the last byte, since that would open up
@@ -1821,13 +2026,18 @@
 	     * byte.
     	     */
 
-	    while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
-	    /* Wait for clean handshake */
-	    while (NCR5380_read(STATUS_REG) & SR_REQ);
-	    d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+	    if (!(hostdata->flags & FLAG_NCR53C400)) {
+		while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+		/* Wait for clean handshake */
+		while (NCR5380_read(STATUS_REG) & SR_REQ);
+		d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+	    }
 	}
     } else {
 	int timeout;
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("About to pwrite %d bytes\n", c);
+#endif
 	if (!(foo = NCR5380_pwrite(instance, d, c))) {
 	    /*
 	     * Wait for the last byte to be sent.  If REQ is being asserted for 
@@ -1868,20 +2078,44 @@
 #endif
 		    }
 		}
-	    } else 
+	    } else  {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+		printk("Waiting for LASTBYTE\n");
+#endif
 		while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+		printk("Got LASTBYTE\n");
+#endif
+	    }
 #else
 	    udelay (5);
 #endif
 	}
     }
 
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
     NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("53C400w: Checking for IRQ\n");
+#endif
+	if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	    printk("53C400w:    got it, reading reset interupt reg\n");
+#endif
+	    NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+	} else {
+	    printk("53C400w:    IRQ NOT THERE!\n");
+	}
+    }
 
     *data = d + c;
     *count = 0;
     *phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
+#if 0
+    NCR5380_print_phase(instance);
+#endif
 #if defined(PSEUDO_DMA) && !defined(UNSAFE)
     sti();
 #endif /* defined(REAL_DMA_POLL) */
@@ -1953,9 +2187,10 @@
 		printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
 		    instance->host_no);
 		sink = 1;
-		msgout = ABORT;
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-		break;
+		do_abort(instance);
+		cmd->result = DID_ERROR  << 16;
+		cmd->done(cmd);
+		return;
 #endif
 		/* 
 		 * If there is no room left in the current buffer in the
@@ -1985,8 +2220,16 @@
 		 */
 
 #if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+		/* KLL
+		 * PSEUDO_DMA is defined here. If this is the g_NCR5380
+		 * driver then it will always be defined, so the
+		 * FLAG_NO_PSEUDO_DMA is used to inhibit PDMA in the base
+		 * NCR5380 case.  I think this is a fairly clean solution.
+		 * We supplement these 2 if's with the flag.
+		 */
 #ifdef NCR5380_dma_xfer_len
 		if (!cmd->device->borken &&
+		    !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
 		    (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
 #else
 		transfersize = cmd->transfersize;
@@ -1997,6 +2240,7 @@
 #endif  /* LIMIT_TRANSFERSIZE */
 
 		if (!cmd->device->borken && transfersize && 
+		    !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
 		    cmd->SCp.this_residual && !(cmd->SCp.this_residual % 
 		    transfersize)) {
 #endif
@@ -2013,7 +2257,9 @@
 			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
 			    ICR_ASSERT_ATN);
 			sink = 1;
-			msgout = ABORT;
+			do_abort(instance);
+			cmd->result = DID_ERROR  << 16;
+			cmd->done(cmd);
 			/* XXX - need to source or sink data here, as appropriate */
 		    } else
 			cmd->SCp.this_residual -= transfersize - len;
@@ -2060,10 +2306,8 @@
 			 printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
 			    instance->host_no, cmd->target, cmd->lun);
 			    sink = 1;
-			    msgout = ABORT;
-			    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
-				ICR_ASSERT_ATN);
-			    break;
+			    do_abort (instance);
+			    return;
 		    }
 
 		    initialize_SCp(cmd->next_link);
@@ -2081,6 +2325,7 @@
 		case ABORT:
 		case COMMAND_COMPLETE: 
 		    /* Accept message by clearing ACK */
+		    sink = 1;
 		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
 		    hostdata->connected = NULL;
 #if (NDEBUG & NDEBUG_QUEUES)
@@ -2130,12 +2375,13 @@
 			cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
 
 			cli();
+			LIST(cmd,hostdata->issue_queue);
 			cmd->host_scribble = (unsigned char *) 
 			    hostdata->issue_queue;
 			hostdata->issue_queue = (Scsi_Cmnd *) cmd;
 			sti();
 #if (NDEBUG & NDEBUG_QUEUES)
-			printk("scsi%d : REQUEST SENSE added to head of issue queue\n");
+			printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no);
 #endif
 		   } else
 #endif /* def AUTOSENSE */
@@ -2169,6 +2415,7 @@
 		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
 		    cmd->device->disconnect = 1;
 		    cli();
+		    LIST(cmd,hostdata->disconnected_queue);
 		    cmd->host_scribble = (unsigned char *) 
 			hostdata->disconnected_queue;
 		    hostdata->connected = NULL;
@@ -2190,6 +2437,9 @@
 		    /* Wait for bus free to avoid nasty timeouts */
 		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
 		    	barrier();
+#if 0
+		    NCR5380_print_status(instance);
+#endif
 		    return;
 		/* 
 		 * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
@@ -2287,7 +2537,7 @@
 		    else
 			printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
 			    instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);
-   
+
 		    msgout = MESSAGE_REJECT;
 		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
 			ICR_ASSERT_ATN);
@@ -2462,10 +2712,13 @@
 	    && (tag == tmp->tag) 
 #endif
 ) {
-	    if (prev)
+	    if (prev) {
+		REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
 		prev->host_scribble = tmp->host_scribble;
-	    else
+	    } else {
+		REMOVE(-1,hostdata->disconnected_queue,tmp,tmp->host_scribble);
 		hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+	    }
 	    tmp->host_scribble = NULL;
 	    break;
 	}
@@ -2487,16 +2740,11 @@
     }
 
     if (abort) {
-	msg[0] = ABORT;
-	len = 1;
-	data = msg;
-	phase = PHASE_MSGOUT;
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-	NCR5380_transfer_pio(instance, &phase, &len, &data);
+	do_abort (instance);
     } else {
 	hostdata->connected = tmp;
 #if (NDEBUG & NDEBUG_RESELECTION)
-	printk"scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
+	printk("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
 	    instance->host_no, tmp->target, tmp->lun, tmp->tag);
 #endif
     }
@@ -2576,8 +2824,11 @@
     struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
 	instance->hostdata;
     Scsi_Cmnd *tmp, **prev;
-    unsigned char msg, phase, *msgptr;
-    int len;
+
+    printk("scsi%d : aborting command\n", instance->host_no);
+    print_Scsi_Cmnd (cmd);
+
+    NCR5380_print_status (instance);
 
     printk("scsi%d : aborting command\n", instance->host_no);
     print_Scsi_Cmnd (cmd);
@@ -2630,11 +2881,16 @@
  * Case 2 : If the command hasn't been issued yet, we simply remove it 
  * 	    from the issue queue.
  */
+#if (NDEBUG & NDEBUG_ABORT)
+    /* KLL */
+    printk("scsi%d : abort going into loop.\n", instance->host_no);
+#endif
     for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), 
 	tmp = (Scsi_Cmnd *) hostdata->issue_queue;
 	tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp = 
 	(Scsi_Cmnd *) tmp->host_scribble) 
 	if (cmd == tmp) {
+	    REMOVE(5,*prev,tmp,tmp->host_scribble);
 	    (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
 	    tmp->host_scribble = NULL;
 	    tmp->result = DID_ABORT << 16;
@@ -2646,6 +2902,10 @@
 	    tmp->done(tmp);
 	    return SCSI_ABORT_SUCCESS;
 	}
+#if (NDEBUG  & NDEBUG_ABORT)
+    /* KLL */
+    	else if (prev == tmp) printk("scsi%d : LOOP\n", instance->host_no);
+#endif
 
 /* 
  * Case 3 : If any commands are connected, we're going to fail the abort
@@ -2706,12 +2966,7 @@
     printk("scsi%d : nexus reestablished.\n", instance->host_no);
 #endif
 
-	    msg = ABORT;
-	    msgptr = &msg;
-	    len = 1;
-	    phase = PHASE_MSGOUT;
-	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-	    NCR5380_transfer_pio (instance, &phase, &len, &msgptr);
+	    do_abort (instance);
 
 	    cli();
 	    for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), 
@@ -2719,6 +2974,7 @@
 		tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp = 
 		(Scsi_Cmnd *) tmp->host_scribble) 
 		    if (cmd == tmp) {
+		    REMOVE(5,*prev,tmp,tmp->host_scribble);
 		    *prev = (Scsi_Cmnd *) tmp->host_scribble;
 		    tmp->host_scribble = NULL;
 		    tmp->result = DID_ABORT << 16;
@@ -2762,12 +3018,7 @@
     NCR5380_setup(cmd->host);
 
     NCR5380_print_status (cmd->host);
-
-    cli();
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
-    udelay(25);
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-    sti();
+    do_reset (cmd->host);
 
     return SCSI_RESET_WAKEUP;
 }
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h
--- v1.3.59/linux/drivers/scsi/NCR5380.h	Thu Jun 29 19:02:47 1995
+++ linux/drivers/scsi/NCR5380.h	Thu Jan 25 20:54:42 1996
@@ -29,9 +29,7 @@
 #define NCR5380_H
 
 #define NCR5380_PUBLIC_RELEASE 6
-#ifdef NCR53C400
-#define NCR53C400_PUBLIC_RELEASE 1
-#endif
+#define NCR53C400_PUBLIC_RELEASE 2
 
 #define NDEBUG_ARBITRATION	0x1
 #define NDEBUG_AUTOSENSE	0x2
@@ -53,6 +51,9 @@
 #define NDEBUG_LAST_BYTE_SENT	0x20000
 #define NDEBUG_RESTART_SELECT	0x40000
 #define NDEBUG_EXTENDED		0x80000
+#define NDEBUG_C400_PREAD	0x100000
+#define NDEBUG_C400_PWRITE	0x200000
+#define NDEBUG_LISTS		0x400000
 
 /* 
  * The contents of the OUTPUT DATA register are asserted on the bus when
@@ -158,31 +159,32 @@
 /* Write any value to this register to start an ini mode DMA receive */
 #define START_DMA_INITIATOR_RECEIVE_REG 7	/* wo */
 
-#ifdef NCR53C400
-#define C400_CONTROL_STATUS_REG                -8      /* rw */
+#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8      /* rw */
 
 #define CSR_RESET              0x80    /* wo  Resets 53c400 */
 #define CSR_53C80_REG          0x80    /* ro  5380 registers busy */
 #define CSR_TRANS_DIR          0x40    /* rw  Data transfer direction */
 #define CSR_SCSI_BUFF_INTR     0x20    /* rw  Enable int on transfer ready */
 #define CSR_53C80_INTR         0x10    /* rw  Enable 53c80 interrupts */
-#define CSR_SHARED_INTR                0x08    /* rw  Interrupt sharing */
+#define CSR_SHARED_INTR        0x08    /* rw  Interrupt sharing */
 #define CSR_HOST_BUF_NOT_RDY   0x04    /* ro  Is Host buffer ready */
 #define CSR_SCSI_BUF_RDY       0x02    /* ro  SCSI buffer read */
 #define CSR_GATED_53C80_IRQ    0x01    /* ro  Last block xferred */
 
+#if 0
 #define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
+#else
+#define CSR_BASE CSR_53C80_INTR
+#endif
 
 /* Number of 128-byte blocks to be transferred */
-#define C400_CLOCK_COUNTER_REG         -7      /* rw */
+#define C400_BLOCK_COUNTER_REG   NCR53C400_register_offset-7      /* rw */
 
 /* Resume transfer after disconnect */
-#define C400_RESUME_TRANSFER_REG       -6      /* wo */
+#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6      /* wo */
 
 /* Access to host buffer stack */
-#define C400_HOST_BUFFER                       -4      /* rw */
-
-#endif /* NCR53C400 */
+#define C400_HOST_BUFFER         NCR53C400_register_offset-4      /* rw */
 
 
 /* Note : PHASE_* macros are based on the values of the STATUS register */
@@ -237,6 +239,7 @@
 #define FLAG_HAS_LAST_BYTE_SENT		1	/* NCR53c81 or better */
 #define FLAG_CHECK_LAST_BYTE_SENT	2	/* Only test once */
 #define FLAG_NCR53C400			4	/* NCR53c400 */
+#define FLAG_NO_PSEUDO_DMA		8	/* Inhibit DMA */
 
 #ifndef ASM
 struct NCR5380_hostdata {
@@ -275,6 +278,8 @@
 static void NCR5380_intr (int irq, struct pt_regs * regs);
 static void NCR5380_main (void);
 static void NCR5380_print_options (struct Scsi_Host *instance);
+static void NCR5380_print_phase (struct Scsi_Host *instance);
+static void NCR5380_print (struct Scsi_Host *instance);
 #ifndef NCR5380_abort
 static
 #endif
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/README.g_NCR5380 linux/drivers/scsi/README.g_NCR5380
--- v1.3.59/linux/drivers/scsi/README.g_NCR5380	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/README.g_NCR5380	Thu Jan 25 20:54:42 1996
@@ -0,0 +1,46 @@
+REAME file for the Linux g_NCR5380 driver.
+
+(c) 1993 Drew Eckhard
+NCR53c400 extensions (c) 1994,1995,1996 Kevin Lentin
+
+This file documents the NCR53c400 extensions by Kevin Lentin and some
+enhancements to the NCR5380 core.
+
+This driver supports both NCR5380 and NCR53c400 cards in port or memory
+mapped modes. Currently this driver can only support one of those mapping
+modes at a time but it does support both of these chips at the same time.
+The next release of this driver will support port & memory mapped cards at
+the same time. It should be able to handle multiple different cards in the
+same machine.
+
+The drivers/scsi/Makefile has an override in it for the most common
+NCR53c400 card, the Trantor T130B in its default configuration:
+	Port: 0x350
+	IRQ : 5
+
+The NCR53c400 does not support DMA but it does have Pseudo-DMA which is
+supported by the driver.
+
+If the default configuration does not work for you, you can use the kernel
+command lines (eg using the lilo append command):
+	ncr5380=port,irq,dma
+	ncr53c400=port,irq
+or
+	ncr5380=base,irq,dma
+	ncr53c400=base,irq
+
+The driver does not probe for any addresses or ports other than those in
+the OVERRIDE or given to the kernel as above.
+
+This driver provides some information on what it has detected in
+/proc/scsi/g_NCR5380/x where x is the scsi card number as detected at boot
+time. More info to come in the future.
+
+When NCR53c400 support is compiled in, BIOS paramaters will be returned by
+the driver (the raw 5380 driver does not and I don't plan to fiddle with
+it!).
+
+This driver works as a module.
+
+Kevin Lentin
+K.Lentin@cs.monash.edu.au
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c
--- v1.3.59/linux/drivers/scsi/g_NCR5380.c	Sun Dec 17 11:43:20 1995
+++ linux/drivers/scsi/g_NCR5380.c	Thu Jan 25 20:54:42 1996
@@ -1,5 +1,3 @@
-#define AUTOSENSE
-
 /*
  * Generic Generic NCR5380 driver
  *	
@@ -9,6 +7,9 @@
  *	drew@colorado.edu
  *      +1 (303) 440-4894
  *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ *    K.Lentin@cs.monash.edu.au
+ *
  * ALPHA RELEASE 1. 
  *
  * For more information, please consult 
@@ -43,9 +44,9 @@
  *     used on the LILO command line to override the defaults.
  *
  * 2.  With the GENERIC_NCR5380_OVERRIDE compile time define.  This is 
- *     specified as an array of address, irq tuples.  Ie, for
- *     one board at the default 0xcc000 address, IRQ5, no dma, I could 
- *     say  -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE}}
+ *     specified as an array of address, irq, dma, board tuples.  Ie, for
+ *     one board at 0x350, IRQ5, no dma, I could say  
+ *     -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
  * 
  * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an 
  * 	IRQ line if overridden on the command line.
@@ -56,6 +57,21 @@
  */
 
 #define AUTOPROBE_IRQ
+#define AUTOSENSE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_GENERIC_NCR53C400
+#define NCR53C400_PSEUDO_DMA 1
+#define PSEUDO_DMA
+#define NCR53C400
+#endif
+#if defined(CONFIG_SCSI_G_NCR5380_PORT) && defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You can not configure the Generic NCR 5380 SCSI Driver for memory mapped I/O and port mapped I/O at the same time (yet)
+#endif
+#if !defined(CONFIG_SCSI_G_NCR5380_PORT) && !defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You must configure the Generic NCR 5380 SCSI Driver for one of memory mapped I/O and port mapped I/O.
+#endif
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -67,6 +83,7 @@
 #include "g_NCR5380.h"
 #include "NCR5380.h"
 #include "constants.h"
+#include "sd.h"
 #include<linux/stat.h>
 
 struct proc_dir_entry proc_scsi_g_ncr5380 = {
@@ -75,9 +92,10 @@
 };
 
 static struct override {
-    int port;
+	NCR5380_implementation_fields;
     int irq;
     int dma;
+    int board;	/* Use NCR53c400, Ricoh, etc. extensions ? */
 } overrides 
 #ifdef GENERIC_NCR5380_OVERRIDE 
     [] = GENERIC_NCR5380_OVERRIDE
@@ -88,26 +106,65 @@
 #define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
 
 /*
- * Function : generic_NCR5380_setup(char *str, int *ints)
+ * Function : static internal_setup(int board, char *str, int *ints)
  *
  * Purpose : LILO command line initialization of the overrides array,
  * 
- * Inputs : str - unused, ints - array of integer parameters with ints[0]
- *	equal to the number of ints.
+ * Inputs : board - either BOARD_NCR5380 for a normal NCR5380 board, 
+ * 	or BOARD_NCR53C400 for a NCR53C400 board. str - unused, ints - 
+ *	array of integer parameters with ints[0] equal to the number of ints.
  *
  */
 
-void generic_NCR5380_setup(char *str, int *ints) {
+static void internal_setup(int board, char *str, int *ints) {
     static int commandline_current = 0;
-    if (ints[0] != 2) 
-	printk("generic_NCR5380_setup : usage ncr5380=port,irq,dma\n");
-    else 
-	if (commandline_current < NO_OVERRIDES) {
-	    overrides[commandline_current].port = ints[1];
-	    overrides[commandline_current].irq = ints[2];
+    switch (board) {
+    case BOARD_NCR5380:
+	if (ints[0] != 2 && ints[0] != 3) 
+	    printk("generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
+	return;
+    case BOARD_NCR53C400:
+	if (ints[0] != 2)
+	    printk("generic_NCR53C400_setup : usage ncr53c400= " STRVAL(NCR5380_map_name) ",irq\n");
+	return;
+    }
+
+    if (commandline_current < NO_OVERRIDES) {
+	overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type)ints[1];
+	overrides[commandline_current].irq = ints[2];
+	if (ints[0] == 3) 
 	    overrides[commandline_current].dma = ints[3];
+	else 
+	    overrides[commandline_current].dma = DMA_NONE;
+	overrides[commandline_current].board = board;
 	    ++commandline_current;
-	}
+    }
+}
+
+/*
+ * Function : generic_NCR5380_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : str - unused, ints - array of integer paramters with ints[0] 
+ * 	equal to the number of ints.
+ */
+
+void generic_NCR5380_setup (char *str, int *ints) {
+    internal_setup (BOARD_NCR5380, str, ints);
+}
+
+/*
+ * Function : generic_NCR53C400_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : str - unused, ints - array of integer paramters with ints[0] 
+ * 	equal to the number of ints.
+ */
+
+void generic_NCR53C400_setup (char *str, int *ints) {
+    internal_setup (BOARD_NCR53C400, str, ints);
 }
 
 /* 
@@ -125,18 +182,28 @@
 int generic_NCR5380_detect(Scsi_Host_Template * tpnt) {
     static int current_override = 0;
     int count;
+    int flags = 0;
     struct Scsi_Host *instance;
 
     tpnt->proc_dir = &proc_scsi_g_ncr5380;
 
     for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
-	if (!(overrides[current_override].port))
+	if (!(overrides[current_override].NCR5380_map_name))
 	    continue;
 
+	switch (overrides[current_override].board) {
+	case BOARD_NCR5380:
+	    flags = FLAG_NO_PSEUDO_DMA;
+	    break;
+	case BOARD_NCR53C400:
+	    flags = FLAG_NCR53C400;
+	    break;
+	}
+
 	instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
-	instance->io_port = overrides[current_override].port;
+	instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
 
-	NCR5380_init(instance, 0);
+	NCR5380_init(instance, flags);
 
 	if (overrides[current_override].irq != IRQ_AUTO)
 	    instance->irq = overrides[current_override].irq;
@@ -155,7 +222,7 @@
 	    printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
 	}
 
-	printk("scsi%d : at port %d", instance->host_no, instance->io_port);
+	printk("scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int)instance->NCR5380_instance_name);
 	if (instance->irq == IRQ_NONE)
 	    printk (" interrupts disabled");
 	else 
@@ -172,9 +239,329 @@
 }
 
 const char * generic_NCR5380_info (void) {
-    static const char string[]="";
+    static const char string[]="Generic NCR5380/53C400 Info";
     return string;
 }
+
+int generic_NCR5380_release_resources(struct Scsi_Host * instance)
+{
+    NCR5380_local_declare();
+
+    NCR5380_setup(instance);
+
+    free_irq(instance->irq);
+
+	return 0;
+}
+
+#ifdef BIOSPARAM
+/*
+ * Function : int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatable H-C-S mapping for 
+ *	the specified device / size.
+ * 
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ *	major / minor, ip[] = {heads, sectors, cylinders}  
+ *
+ * Returns : allways 0 (success), initializes ip
+ *	
+ */
+
+/* 
+ * XXX Most SCSI boards use this mapping, I could be incorrect.  Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+{
+  int size = disk->capacity;
+  ip[0] = 64;
+  ip[1] = 32;
+  ip[2] = size >> 11;
+  return 0;
+}
+#endif
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
+{
+	int len = 0;
+	struct Scsi_Host *scsi_ptr;
+
+	for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next)
+		if (scsi_ptr->host_no == hostno)
+			break;
+
+	len += sprintf(buffer+len, "SCSI host number %d : %s\n", scsi_ptr->host_no,  scsi_ptr->hostt->name);
+	len += sprintf(buffer+len, "Generic NCR5380 driver version %d\n", GENERIC_NCR5380_PUBLIC_RELEASE);
+	len += sprintf(buffer+len, "NCR5380 driver core version %d\n", NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+	len += sprintf(buffer+len, "NCR53C400 driver extension version %d\n", NCR53C400_PUBLIC_RELEASE);
+	len += sprintf(buffer+len, "NCR53C400 card%s detected\n",  (((struct NCR5380_hostdata *)scsi_ptr->hostdata)->flags & FLAG_NCR53C400)?"":" not");
+# if NCR53C400_PSEUDO_DMA
+	len += sprintf(buffer+len, "NCR53C400 pseudo DMA being used\n");
+# endif
+#else
+	len += sprintf(buffer+len, "NO NCR53C400 driver extensions\n");
+#endif
+	len += sprintf(buffer+len, "Using %s mapping at %s 0x%x, ", STRVAL(NCR5380_map_config), STRVAL(NCR5380_map_name), scsi_ptr->NCR5380_instance_name);
+	if (scsi_ptr->irq == IRQ_NONE)
+		len += sprintf(buffer+len, "interrupts disabled\n");
+	else
+		len += sprintf(buffer+len, "on interrupt %d\n", scsi_ptr->irq);
+
+	*start = buffer + offset;
+	len -= offset;
+	if (len > length)
+		len = length;
+	return len;
+}
+
+#if NCR53C400_PSEUDO_DMA
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,    int len)
+{
+    int blocks = len / 128;
+    int start = 0;
+    int i;
+    int bl;
+    NCR5380_local_declare();
+
+    NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    printk("53C400r: About to read %d blocks for %d bytes\n", blocks, len);
+#endif
+
+    NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
+    NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+    while (1) {
+    
+#if (NDEBUG & NDEBUG_C400_PREAD)
+	printk("53C400r: %d blocks left\n", blocks);
+#endif
+
+	if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+	    if (blocks)
+		printk("53C400r: blocks still == %d\n", blocks);
+	    else
+		printk("53C400r: Exiting loop\n");
+#endif
+	    break;
+	}
+
+#if 1
+	if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+	    printk("53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+	    return -1;
+	}
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+	printk("53C400r: Waiting for buffer, bl=%d\n", bl);
+#endif
+
+	while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+	    ;
+#if (NDEBUG & NDEBUG_C400_PREAD)
+	printk("53C400r: Transferring 128 bytes\n");
+#endif
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+	for (i=0; i<128; i++)
+	    dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+	/* implies CONFIG_SCSI_G_NCR5380_MEM */
+	memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+	start+=128;
+	blocks--;
+    }
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    printk("53C400r: EXTRA: Waiting for buffer\n");
+#endif
+    while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+	;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    printk("53C400r: Transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+	for (i=0; i<128; i++)
+	    dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+	/* implies CONFIG_SCSI_G_NCR5380_MEM */
+	memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+    start+=128;
+    blocks--;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    printk("53C400r: Final values: blocks=%d   start=%d\n", blocks, start);
+#endif
+
+    if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+	printk("53C400r: no 53C80 gated irq after transfer");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    else
+	printk("53C400r: Got 53C80 interupt and tried to clear it\n");
+#endif
+
+/* DON'T DO THIS - THEY NEVER ARRIVE!
+    printk("53C400r: Waiting for 53C80 registers\n");
+    while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+	;
+*/
+
+    if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
+	printk("53C400r: no end dma signal\n");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+    else
+	printk("53C400r: end dma as expected\n");
+#endif
+
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+    return 0;
+}
+		
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,    int len)
+{
+    int blocks = len / 128;
+    int start = 0;
+    int i;
+    int bl;
+    NCR5380_local_declare();
+
+    NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+    printk("53C400w: About to write %d blocks for %d bytes\n", blocks, len);
+#endif
+
+    NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+    NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+    while (1) {
+	if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+	    printk("53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+	    return -1;
+	}
+
+	if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	    if (blocks)
+		printk("53C400w: exiting loop, blocks still == %d\n", blocks);
+	    else
+		printk("53C400w: exiting loop\n");
+#endif
+	    break;
+	}
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("53C400w: %d blocks left\n", blocks);
+
+	printk("53C400w: waiting for buffer, bl=%d\n", bl);
+#endif
+	while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+	    ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("53C400w: transferring 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+	for (i=0; i<128; i++)
+	    NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+	/* implies CONFIG_SCSI_G_NCR5380_MEM */
+	memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+	start+=128;
+	blocks--;
+    }
+    if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("53C400w: EXTRA waiting for buffer\n");
+#endif
+	while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+	    ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	printk("53C400w: transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+	for (i=0; i<128; i++)
+	    NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+	/* implies CONFIG_SCSI_G_NCR5380_MEM */
+	memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+	start+=128;
+	blocks--;
+    }
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+    else
+	printk("53C400w: No EXTRA required\n");
+#endif
+    
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+    printk("53C400w: Final values: blocks=%d   start=%d\n", blocks, start);
+#endif
+
+#if 0
+    printk("53C400w: waiting for registers to be available\n");
+    THEY NEVER DO!
+    while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+	;
+    printk("53C400w: Got em\n");
+#endif
+
+    /* Let's wait for this instead - could be ugly */
+    /* All documentation says to check for this. Maybe my hardware is too
+     * fast. Waiting for it seems to work fine! KLL
+     */
+    while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+	;
+
+    /*
+     * I know. i is certainly != 0 here but the loop is new. See previous
+     * comment.
+     */
+    if (i) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	prink("53C400w: got 53C80 gated irq (last block)\n");
+#endif
+	if (!((i=NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
+	    printk("53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n",i);
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+	else
+	    printk("53C400w: Got END OF DMA\n");
+#endif
+    }
+    else
+	printk("53C400w: no 53C80 gated irq after transfer (last block)\n");
+
+#if 0
+    if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
+	printk("53C400w: no end dma signal\n");
+    }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+    printk("53C400w: waiting for last byte...\n");
+#endif
+    while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+    	;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+    printk("53C400w:     got last byte.\n");
+    printk("53C400w: pwrite exiting with status 0, whoopee!\n");
+#endif
+    return 0;
+}
+#endif /* PSEUDO_DMA */
 
 #include "NCR5380.c"
 #ifdef MODULE
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h
--- v1.3.59/linux/drivers/scsi/g_NCR5380.h	Sun Dec 17 11:43:20 1995
+++ linux/drivers/scsi/g_NCR5380.h	Thu Jan 25 20:54:42 1996
@@ -7,6 +7,9 @@
  *	drew@colorado.edu
  *      +1 (303) 440-4894
  *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ *    K.Lentin@cs.monash.edu.au
+ *
  * ALPHA RELEASE 1. 
  *
  * For more information, please consult 
@@ -31,13 +34,24 @@
 
 #define GENERIC_NCR5380_PUBLIC_RELEASE 1
 
+#ifdef NCR53C400
+#define BIOSPARAM
+#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
+#else
+#define NCR5380_BIOSPARAM NULL
+#endif
 
 #ifndef ASM
 int generic_NCR5380_abort(Scsi_Cmnd *);
 int generic_NCR5380_detect(Scsi_Host_Template *);
+int generic_NCR5380_release_resources(struct Scsi_Host *);
 int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int generic_NCR5380_reset(Scsi_Cmnd *);
+#ifdef BIOSPARAM
+int generic_NCR5380_biosparam(Disk *, kdev_t, int *);
+#endif
 
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout);
 
 #ifndef NULL
 #define NULL 0
@@ -53,36 +67,91 @@
 
 #if defined(HOSTS_C) || defined(MODULE)
 
-#define GENERIC_NCR5380 {NULL, NULL, NULL, NULL, \
-	"Trantor T128/T128F/T228", 		\
-	generic_NCR5380_detect, NULL, NULL, NULL, 			\
+#define GENERIC_NCR5380 {NULL, NULL, NULL, 	 			\
+	generic_NCR5380_proc_info,					\
+	"Generic NCR5380/NCR53C400 Scsi Driver", 			\
+	generic_NCR5380_detect, generic_NCR5380_release_resources,	\
+	generic_NCR5380_info, NULL,					\
 	generic_NCR5380_queue_command, generic_NCR5380_abort, 		\
-	generic_NCR5380_reset, NULL, 					\
-	NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,		\
+	generic_NCR5380_reset, NULL,					\
+	NCR5380_BIOSPARAM,						\
+	/* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,			\
 	/* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
 
 #endif
 
 #ifndef HOSTS_C
 
+#define __STRVAL(x) #x
+#define STRVAL(x) __STRVAL(x)
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+
+#define NCR5380_map_config port
+
+#define NCR5380_map_type int
+
+#define NCR5380_map_name port
+
+#define NCR5380_instance_name io_port
+
+#define NCR53C400_register_offset 0
+
+#define NCR53C400_address_adjust 8
+
+#ifdef NCR53C400
+#define NCR5380_region_size 16
+#else
+#define NCR5380_region_size 8
+#endif
+
+#define NCR5380_read(reg) (inb(NCR5380_map_name + (reg)))
+#define NCR5380_write(reg, value) (outb((value), (NCR5380_map_name + (reg))))
+
+#else 
+/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+
+#define NCR5380_map_config memory
+
+#define NCR5380_map_type volatile unsigned char*
+
+#define NCR5380_map_name base
+
+#define NCR5380_instance_name base
+
+#define NCR53C400_register_offset 0x108
+
+#define NCR53C400_address_adjust 0
+
+#define NCR53C400_mem_base 0x3880
+
+#define NCR53C400_host_buffer 0x3900
+
+#define NCR5380_region_size 0x3a00
+
+
+#define NCR5380_read(reg) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)))
+#define NCR5380_write(reg, value) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)) = value)
+
+#endif
+
 #define NCR5380_implementation_fields \
-    int port
+    NCR5380_map_type NCR5380_map_name
 
 #define NCR5380_local_declare() \
-    register int port
+    register NCR5380_implementation_fields
 
 #define NCR5380_setup(instance) \
-    port = (instance)->io_port
-
-#define NCR5380_read(reg) (inb(port + (reg)))
-#define NCR5380_write(reg, value) (outb((value), (port + (reg))))
+    NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
 
 #define NCR5380_intr generic_NCR5380_intr
 #define NCR5380_queue_command generic_NCR5380_queue_command
 #define NCR5380_abort generic_NCR5380_abort
 #define NCR5380_reset generic_NCR5380_reset
+#define NCR5380_pread generic_NCR5380_pread
+#define NCR5380_pwrite generic_NCR5380_pwrite
 
-#define BOARD_NORMAL	0
+#define BOARD_NCR5380	0
 #define BOARD_NCR53C400	1
 
 #endif /* else def HOSTS_C */
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.59/linux/drivers/scsi/scsi.c	Tue Jan 23 21:15:43 1996
+++ linux/drivers/scsi/scsi.c	Wed Feb  7 08:55:39 1996
@@ -82,8 +82,9 @@
                  struct Scsi_Host *shpnt, char * scsi_result);
 void scsi_build_commandblocks(Scsi_Device * SDpnt);
 
+#ifdef CONFIG_MODULES
 extern struct symbol_table scsi_symbol_table;
-
+#endif
 
 static FreeSectorBitmap * dma_malloc_freelist = NULL;
 static int scsi_need_isa_bounce_buffers;
@@ -2331,7 +2332,9 @@
     timer_table[SCSI_TIMER].fn = scsi_main_timeout;
     timer_table[SCSI_TIMER].expires = 0;
 
+#ifdef CONFIG_MODULES
     register_symtab(&scsi_symbol_table);
+#endif    
 
     /* Register the /proc/scsi/scsi entry */
 #if CONFIG_PROC_FS 
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v1.3.59/linux/drivers/scsi/scsi.h	Wed Dec 13 09:02:46 1995
+++ linux/drivers/scsi/scsi.h	Thu Jan 25 20:54:42 1996
@@ -16,7 +16,7 @@
 #define _SCSI_H
 
 /*
- * Some of the public constants are being movedd to this file.
+ * Some of the public constants are being moved to this file.
  * We include it here so that what came from where is transparent.
  */
 #include <linux/scsi.h>
diff -u --recursive --new-file v1.3.59/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.59/linux/drivers/scsi/scsi_syms.c	Wed Dec 13 09:02:46 1995
+++ linux/drivers/scsi/scsi_syms.c	Wed Feb  7 08:55:39 1996
@@ -5,6 +5,7 @@
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
+#ifdef CONFIG_MODULES
 
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -59,6 +60,7 @@
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
     X(request_queueable),
+    X(print_Scsi_Cmnd),
 #if defined(CONFIG_PROC_FS)
     X(proc_print_scsidevice),
 #endif
@@ -76,3 +78,4 @@
      */
 #include <linux/symtab_end.h>
 };
+#endif
diff -u --recursive --new-file v1.3.59/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.3.59/linux/drivers/sound/sb_dsp.c	Fri Jan 26 01:37:07 1996
+++ linux/drivers/sound/sb_dsp.c	Thu Jan 25 20:54:42 1996
@@ -1313,6 +1313,7 @@
   sb_setmixer (0x40, cfg | 0x03);
 }
 
+#ifdef CONFIG_MIDI
 void
 ess_midi_init (struct address_info *hw_config)	/* called from sb16_midi.c */
 {
@@ -1360,6 +1361,7 @@
 
   sb_setmixer (0x40, cfg);
 }
+#endif
 
 void
 Jazz16_midi_init (struct address_info *hw_config)
diff -u --recursive --new-file v1.3.59/linux/fs/Config.in linux/fs/Config.in
--- v1.3.59/linux/fs/Config.in	Tue Jan 23 21:15:49 1996
+++ linux/fs/Config.in	Wed Feb  7 10:01:38 1996
@@ -9,10 +9,13 @@
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 tristate 'xiafs filesystem support' CONFIG_XIA_FS
-tristate 'msdos fs support' CONFIG_MSDOS_FS
-if [ "$CONFIG_MSDOS_FS" != "n" ]; then
-  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS
-fi
+
+# msdos filesystems
+tristate 'DOS FAT fs support' CONFIG_FAT_FS
+dep_tristate 'Old MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate 'vfat (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+
 bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
diff -u --recursive --new-file v1.3.59/linux/fs/Makefile linux/fs/Makefile
--- v1.3.59/linux/fs/Makefile	Tue Jan  2 16:46:27 1996
+++ linux/fs/Makefile	Wed Feb  7 09:39:27 1996
@@ -16,8 +16,8 @@
 		dcache.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
-ALL_SUB_DIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv \
-		smbfs ncpfs
+ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
+		hpfs sysv smbfs ncpfs
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -49,11 +49,27 @@
   endif
 endif
 
+ifeq ($(CONFIG_FAT_FS),y)
+SUB_DIRS += fat
+else
+  ifeq ($(CONFIG_MSDOS_FS),m)
+  MOD_SUB_DIRS += fat
+  endif
+endif
+
 ifeq ($(CONFIG_MSDOS_FS),y)
 SUB_DIRS += msdos
 else
   ifeq ($(CONFIG_MSDOS_FS),m)
   MOD_SUB_DIRS += msdos
+  endif
+endif
+
+ifeq ($(CONFIG_VFAT_FS),y)
+SUB_DIRS += vfat
+else
+  ifeq ($(CONFIG_VFAT_FS),m)
+  MOD_SUB_DIRS += vfat
   endif
 endif
 
diff -u --recursive --new-file v1.3.59/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.59/linux/fs/buffer.c	Fri Jan 26 01:37:07 1996
+++ linux/fs/buffer.c	Wed Feb  7 08:55:39 1996
@@ -1228,7 +1228,13 @@
 	int *p, nr[PAGE_SIZE/512];
 	int i;
 
+	address = page_address(page);
+	page->count++;
 	wait_on_page(page);
+	if (page->uptodate) {
+		free_page(address);
+		return 0;
+	}
 	page->locked = 1;
 	
 	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
@@ -1242,8 +1248,6 @@
 	} while (i > 0);
 
 	/* IO start */
-	page->count++;
-	address = page_address(page);
 	brw_page(READ, address, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
 	free_page(address);
 	return 0;
diff -u --recursive --new-file v1.3.59/linux/fs/exec.c linux/fs/exec.c
--- v1.3.59/linux/fs/exec.c	Sun Jan 14 16:30:12 1996
+++ linux/fs/exec.c	Wed Feb  7 08:55:39 1996
@@ -803,7 +803,13 @@
 			break;
 #ifdef CONFIG_KERNELD
 		}else{
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
 			char modname[20];
+			if (printable(bprm.buf[0]) &&
+			    printable(bprm.buf[1]) &&
+			    printable(bprm.buf[2]) &&
+			    printable(bprm.buf[3]))
+				break; /* -ENOEXEC */
 			sprintf(modname, "binfmt-%hd", *(short*)(&bprm.buf));
 			request_module(modname);
 #endif
diff -u --recursive --new-file v1.3.59/linux/fs/fat/Makefile linux/fs/fat/Makefile
--- v1.3.59/linux/fs/fat/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/Makefile	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux fat-filesystem support.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := fat.o
+O_OBJS   := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
+OX_OBJS  := fatfs_syms.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.59/linux/fs/fat/buffer.c linux/fs/fat/buffer.c
--- v1.3.59/linux/fs/fat/buffer.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/buffer.c	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,150 @@
+/*
+ * linux/fs/msdos/buffer.c
+ *
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+
+struct buffer_head *fat_bread (
+	struct super_block *sb,
+	int block)
+{
+	struct buffer_head *ret = NULL;
+	if (sb->s_blocksize == 512){
+		ret = bread (sb->s_dev,block,512);
+	}else{
+		struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+		if (real != NULL){
+			ret = (struct buffer_head *)kmalloc (sizeof(struct buffer_head)
+				,GFP_KERNEL);
+			if (ret != NULL){
+				/* #Specification: msdos / strategy / special device / dummy blocks
+					Many special device (Scsi optical disk for one) use
+					larger hardware sector size. This allows for higher
+					capacity.
+
+					Most of the time, the MsDOS file system that sit
+					on this device is totally unaligned. It use logically
+					512 bytes sector size, with logical sector starting
+					in the middle of a hardware block. The bad news is
+					that a hardware sector may hold data own by two
+					different files. This means that the hardware sector
+					must be read, patch and written almost all the time.
+
+					Needless to say that it kills write performance
+					on all OS.
+
+					Internally the linux msdos fs is using 512 bytes
+					logical sector. When accessing such a device, we
+					allocate dummy buffer cache blocks, that we stuff
+					with the information of a real one (1k large).
+
+					This strategy is used to hide this difference to
+					the core of the msdos fs. The slowdown is not
+					hidden though!
+				*/
+				/*
+					The memset is there only to catch errors. The msdos
+					fs is only using b_data
+				*/
+				memset (ret,0,sizeof(*ret));
+				ret->b_data = real->b_data;
+				if (block & 1) ret->b_data += 512;
+				ret->b_next = real;
+			}else{
+				brelse (real);
+			}
+		}
+	}
+	return ret;
+}
+struct buffer_head *fat_getblk (
+	struct super_block *sb,
+	int block)
+{
+	struct buffer_head *ret = NULL;
+	if (sb->s_blocksize == 512){
+		ret = getblk (sb->s_dev,block,512);
+	}else{
+		/* #Specification: msdos / special device / writing
+			A write is always preceded by a read of the complete block
+			(large hardware sector size). This defeat write performance.
+			There is a possibility to optimize this when writing large
+			chunk by making sure we are filling large block. Volunteer ?
+		*/
+		ret = fat_bread (sb,block);
+	}
+	return ret;
+}
+
+void fat_brelse (
+	struct super_block *sb,
+	struct buffer_head *bh)
+{
+	if (bh != NULL){
+		if (sb->s_blocksize == 512){
+			brelse (bh);
+		}else{
+			brelse (bh->b_next);
+			/* We can free the dummy because a new one is allocated at
+				each fat_getblk() and fat_bread().
+			*/
+			kfree (bh);
+		}
+	}
+}
+	
+void fat_mark_buffer_dirty (
+	struct super_block *sb,
+	struct buffer_head *bh,
+	int dirty_val)
+{
+	if (sb->s_blocksize != 512){
+		bh = bh->b_next;
+	}
+	mark_buffer_dirty (bh,dirty_val);
+}
+
+void fat_set_uptodate (
+	struct super_block *sb,
+	struct buffer_head *bh,
+	int val)
+{
+	if (sb->s_blocksize != 512){
+		bh = bh->b_next;
+	}
+	mark_buffer_uptodate(bh, val);
+}
+int fat_is_uptodate (
+	struct super_block *sb,
+	struct buffer_head *bh)
+{
+	if (sb->s_blocksize != 512){
+		bh = bh->b_next;
+	}
+	return buffer_uptodate(bh);
+}
+
+void fat_ll_rw_block (
+	struct super_block *sb,
+	int opr,
+	int nbreq,
+	struct buffer_head *bh[32])
+{
+	if (sb->s_blocksize == 512){
+		ll_rw_block(opr,nbreq,bh);
+	}else{
+		struct buffer_head *tmp[32];
+		int i;
+		for (i=0; i<nbreq; i++){
+			tmp[i] = bh[i]->b_next;
+		}
+		ll_rw_block(opr,nbreq,tmp);
+	}
+}
+
diff -u --recursive --new-file v1.3.59/linux/fs/fat/cache.c linux/fs/fat/cache.c
--- v1.3.59/linux/fs/fat/cache.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/cache.c	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,297 @@
+/*
+ *  linux/fs/msdos/cache.c
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ */
+
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include "msbuffer.h"
+
+static struct fat_cache *fat_cache,cache[FAT_CACHE];
+
+/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
+   new_value is != -1, that FAT entry is replaced by it. */
+
+int fat_access(struct super_block *sb,int nr,int new_value)
+{
+	struct buffer_head *bh,*bh2,*c_bh,*c_bh2;
+	unsigned char *p_first,*p_last;
+	int first,last,next,copy;
+
+	if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters) return 0;
+	if (MSDOS_SB(sb)->fat_bits == 16) first = last = nr*2;
+	else {
+		first = nr*3/2;
+		last = first+1;
+	}
+	if (!(bh = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >>
+	    SECTOR_BITS),SECTOR_SIZE))) {
+		printk("bread in fat_access failed\n");
+		return 0;
+	}
+	if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS))
+		bh2 = bh;
+	else {
+		if (!(bh2 = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last
+		    >> SECTOR_BITS),SECTOR_SIZE))) {
+			brelse(bh);
+			printk("bread in fat_access failed\n");
+			return 0;
+		}
+	}
+	if (MSDOS_SB(sb)->fat_bits == 16) {
+		p_first = p_last = NULL; /* GCC needs that stuff */
+		next = CF_LE_W(((unsigned short *) bh->b_data)[(first &
+		    (SECTOR_SIZE-1)) >> 1]);
+		if (next >= 0xfff7) next = -1;
+	}
+	else {
+		p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
+		p_last = &((unsigned char *) bh2->b_data)[(first+1) &
+		    (SECTOR_SIZE-1)];
+		if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+		else next = (*p_first+(*p_last << 8)) & 0xfff;
+		if (next >= 0xff7) next = -1;
+	}
+	if (new_value != -1) {
+		if (MSDOS_SB(sb)->fat_bits == 16)
+			((unsigned short *) bh->b_data)[(first & (SECTOR_SIZE-1)) >>
+			    1] = CT_LE_W(new_value);
+		else {
+			if (nr & 1) {
+				*p_first = (*p_first & 0xf) | (new_value << 4);
+				*p_last = new_value >> 4;
+			}
+			else {
+				*p_first = new_value & 0xff;
+				*p_last = (*p_last & 0xf0) | (new_value >> 8);
+			}
+			mark_buffer_dirty(bh2, 1);
+		}
+		mark_buffer_dirty(bh, 1);
+		for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
+			if (!(c_bh = bread(sb->s_dev,MSDOS_SB(sb)->
+			    fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)->
+			    fat_length*copy,SECTOR_SIZE))) break;
+			memcpy(c_bh->b_data,bh->b_data,SECTOR_SIZE);
+			mark_buffer_dirty(c_bh, 1);
+			if (bh != bh2) {
+				if (!(c_bh2 = bread(sb->s_dev,
+				    MSDOS_SB(sb)->fat_start+(first >>
+				    SECTOR_BITS)+MSDOS_SB(sb)->fat_length*copy
+				    +1,SECTOR_SIZE))) {
+					brelse(c_bh);
+					break;
+				}
+				memcpy(c_bh2->b_data,bh2->b_data,SECTOR_SIZE);
+				brelse(c_bh2);
+			}
+			brelse(c_bh);
+		}
+	}
+	brelse(bh);
+	if (bh != bh2) brelse(bh2);
+	return next;
+}
+
+
+void cache_init(void)
+{
+	static int initialized = 0;
+	int count;
+
+	if (initialized) return;
+	fat_cache = &cache[0];
+	for (count = 0; count < FAT_CACHE; count++) {
+		cache[count].device = 0;
+		cache[count].next = count == FAT_CACHE-1 ? NULL :
+		    &cache[count+1];
+	}
+	initialized = 1;
+}
+
+
+void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
+{
+	struct fat_cache *walk;
+
+#ifdef DEBUG
+printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
+       inode->i_ino, cluster, *f_clu, *d_clu);
+#endif
+	for (walk = fat_cache; walk; walk = walk->next)
+		if (inode->i_dev == walk->device
+		    && walk->ino == inode->i_ino
+		    && walk->file_cluster <= cluster
+		    && walk->file_cluster > *f_clu) {
+			*d_clu = walk->disk_cluster;
+#ifdef DEBUG
+printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
+#endif
+			if ((*f_clu = walk->file_cluster) == cluster) return;
+		}
+#ifdef DEBUG
+printk("cache miss\n");
+#endif
+}
+
+
+#ifdef DEBUG
+static void list_cache(void)
+{
+	struct fat_cache *walk;
+
+	for (walk = fat_cache; walk; walk = walk->next) {
+		if (walk->device)
+			printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
+			       walk->ino, walk->file_cluster, walk->disk_cluster);
+		else printk("-- ");
+	}
+	printk("\n");
+}
+#endif
+
+
+void cache_add(struct inode *inode,int f_clu,int d_clu)
+{
+	struct fat_cache *walk,*last;
+
+#ifdef DEBUG
+printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
+       inode->i_ino, f_clu, d_clu);
+#endif
+	last = NULL;
+	for (walk = fat_cache; walk->next; walk = (last = walk)->next)
+		if (inode->i_dev == walk->device
+		    && walk->ino == inode->i_ino
+		    && walk->file_cluster == f_clu) {
+			if (walk->disk_cluster != d_clu) {
+				printk("FAT cache corruption");
+				fat_cache_inval_inode(inode);
+				return;
+			}
+			/* update LRU */
+			if (last == NULL) return;
+			last->next = walk->next;
+			walk->next = fat_cache;
+			fat_cache = walk;
+#ifdef DEBUG
+list_cache();
+#endif
+			return;
+		}
+	walk->device = inode->i_dev;
+	walk->ino = inode->i_ino;
+	walk->file_cluster = f_clu;
+	walk->disk_cluster = d_clu;
+	last->next = NULL;
+	walk->next = fat_cache;
+	fat_cache = walk;
+#ifdef DEBUG
+list_cache();
+#endif
+}
+
+
+/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
+   fixes itself after a while. */
+
+void fat_cache_inval_inode(struct inode *inode)
+{
+	struct fat_cache *walk;
+
+	for (walk = fat_cache; walk; walk = walk->next)
+		if (walk->device == inode->i_dev
+		    && walk->ino == inode->i_ino)
+			walk->device = 0;
+}
+
+
+void fat_cache_inval_dev(kdev_t device)
+{
+	struct fat_cache *walk;
+
+	for (walk = fat_cache; walk; walk = walk->next)
+		if (walk->device == device)
+			walk->device = 0;
+}
+
+
+int get_cluster(struct inode *inode,int cluster)
+{
+	int nr,count;
+
+	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+	if (!cluster) return nr;
+	count = 0;
+	for (cache_lookup(inode,cluster,&count,&nr); count < cluster;
+	    count++) {
+		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+		if (!nr) return 0;
+	}
+	cache_add(inode,cluster,nr);
+	return nr;
+}
+
+
+int fat_smap(struct inode *inode,int sector)
+{
+	struct msdos_sb_info *sb;
+	int cluster,offset;
+
+	sb = MSDOS_SB(inode->i_sb);
+	if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+	    !MSDOS_I(inode)->i_start)) {
+		if (sector >= sb->dir_entries >> MSDOS_DPS_BITS) return 0;
+		return sector+sb->dir_start;
+	}
+	cluster = sector/sb->cluster_size;
+	offset = sector % sb->cluster_size;
+	if (!(cluster = get_cluster(inode,cluster))) return 0;
+	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
+}
+
+
+/* Free all clusters after the skip'th cluster. Doesn't use the cache,
+   because this way we get an additional sanity check. */
+
+int fat_free(struct inode *inode,int skip)
+{
+	int nr,last;
+
+	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+	last = 0;
+	while (skip--) {
+		last = nr;
+		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+		if (!nr) {
+			printk("fat_free: skipped EOF\n");
+			return -EIO;
+		}
+	}
+	if (last)
+		fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits ==
+		    12 ? 0xff8 : 0xfff8);
+	else {
+		MSDOS_I(inode)->i_start = 0;
+		inode->i_dirt = 1;
+	}
+	lock_fat(inode->i_sb);
+	while (nr != -1) {
+		if (!(nr = fat_access(inode->i_sb,nr,0))) {
+			fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
+			break;
+		}
+		if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
+			MSDOS_SB(inode->i_sb)->free_clusters++;
+		inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
+	}
+	unlock_fat(inode->i_sb);
+	fat_cache_inval_inode(inode);
+	return 0;
+}
diff -u --recursive --new-file v1.3.59/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v1.3.59/linux/fs/fat/dir.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/dir.c	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,281 @@
+/*
+ *  linux/fs/msdos/dir.c
+ *  MS-DOS directory handling functions
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ *
+ *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *
+ *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+
+#include <asm/segment.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+
+#define PRINTK(X)
+
+static int fat_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
+{
+	return -EISDIR;
+}
+
+struct file_operations fat_dir_operations = {
+	NULL,			/* lseek - default */
+	fat_dir_read,		/* read */
+	NULL,			/* write - bad */
+	fat_readdir,		/* readdir */
+	NULL,			/* select - default */
+	fat_dir_ioctl,		/* ioctl - default */
+	NULL,			/* mmap */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync		/* fsync */
+};
+
+
+int fat_dir_ioctl(struct inode * inode, struct file * filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+#if 0
+	/*
+	 * We want to provide an interface for Samba to be able
+	 * to get the short filename for a given long filename.
+	 * We should be able to accomplish by modifying fat_readdir
+	 * slightly.
+	 */
+	case VFAT_LONGNAME_TO_SHORT:
+#endif
+	default:
+		return -EINVAL;
+	}
+}
+
+int fat_readdir(
+	struct inode *inode,
+	struct file *filp,
+	void *dirent,
+	filldir_t filldir)
+{
+	struct super_block *sb = inode->i_sb;
+	int ino,i,i2,last;
+	char c;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	unsigned long oldpos = filp->f_pos;
+	int is_long;
+	char longname[256];
+	unsigned char long_len = 0; /* Make compiler warning go away */
+	unsigned char alias_checksum = 0; /* Make compiler warning go away */
+
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		return -EBADF;
+/* Fake . and .. for the root directory. */
+	if (inode->i_ino == MSDOS_ROOT_INO) {
+		while (oldpos < 2) {
+			if (filldir(dirent, "..", oldpos+1, oldpos, MSDOS_ROOT_INO) < 0)
+				return 0;
+			oldpos++;
+			filp->f_pos++;
+		}
+		if (oldpos == 2)
+			filp->f_pos = 0;
+	}
+	if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
+		return -ENOENT;
+
+ 	bh = NULL;
+	longname[0] = '\0';
+	is_long = 0;
+	ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+	while (ino > -1) {
+		/* Should we warn about finding extended entries on fs
+		 * mounted non-vfat ? Deleting or renaming files will cause
+		 * corruption, as extended entries are not updated.
+		 */
+		if (!MSDOS_SB(sb)->vfat && 
+		    !MSDOS_SB(sb)->umsdos &&  /* umsdos is safe for us */
+		    !IS_RDONLY(inode) && 
+		    (de->attr == ATTR_EXT) &&
+		    !MSDOS_SB(sb)->quiet) {
+			printk("MSDOS-fs warning: vfat directory entry found on fs mounted non-vfat (device %s)\n",
+			       kdevname(sb->s_dev));
+		}
+
+		/* Check for long filename entry */
+		if (MSDOS_SB(sb)->vfat && (de->name[0] == (__s8) DELETED_FLAG)) {
+			is_long = 0;
+			oldpos = filp->f_pos;
+		} else if (MSDOS_SB(sb)->vfat && de->attr ==  ATTR_EXT) {
+			int get_new_entry;
+			struct msdos_dir_slot *ds;
+			unsigned char page, pg_off, ascii;
+			unsigned char *uni_page;
+			unsigned char offset;
+			unsigned char id;
+			unsigned char slot;
+			unsigned char slots = 0;
+			int i;
+
+			offset = 0;
+			ds = (struct msdos_dir_slot *) de;
+			id = ds->id;
+			if (id & 0x40) {
+				slots = id & ~0x40;
+				is_long = 1;
+				alias_checksum = ds->alias_checksum;
+			}
+
+			get_new_entry = 1;
+			slot = slots;
+			while (slot > 0) {
+				PRINTK(("1. get_new_entry: %d\n", get_new_entry));
+				if (ds->attr !=  ATTR_EXT) {
+					is_long = 0;
+					get_new_entry = 0;
+					break;
+				}
+				if ((ds->id & ~0x40) != slot) {
+					is_long = 0;
+					break;
+				}
+				if (ds->alias_checksum != alias_checksum) {
+					is_long = 0;
+					break;
+				}
+				slot--;
+				offset = slot * 13;
+				PRINTK(("2. get_new_entry: %d\n", get_new_entry));
+				for (i = 0; i < 10; i += 2) {
+					pg_off = ds->name0_4[i];
+					page = ds->name0_4[i+1];
+					if (pg_off == 0 && page == 0) {
+						goto found_end;
+					}
+					uni_page = fat_uni2asc_pg[page];
+					ascii = uni_page[pg_off];
+					longname[offset++] = ascii ? ascii : '?';
+				}
+				for (i = 0; i < 12; i += 2) {
+					pg_off = ds->name5_10[i];
+					page = ds->name5_10[i+1];
+					if (pg_off == 0 && page == 0) {
+						goto found_end;
+					}
+					uni_page = fat_uni2asc_pg[page];
+					ascii = uni_page[pg_off];
+					longname[offset++] = ascii ? ascii : '?';
+				}
+				for (i = 0; i < 4; i += 2) {
+					pg_off = ds->name11_12[i];
+					page = ds->name11_12[i+1];
+					if (pg_off == 0 && page == 0) {
+						goto found_end;
+					}
+					uni_page = fat_uni2asc_pg[page];
+					ascii = uni_page[pg_off];
+					longname[offset++] = ascii ? ascii : '?';
+				}
+				found_end:
+				PRINTK(("3. get_new_entry: %d\n", get_new_entry));
+				if (ds->id & 0x40) {
+					longname[offset] = '\0';
+					long_len = offset;
+				}
+				if (slot > 0) {
+					ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+					PRINTK(("4. get_new_entry: %d\n", get_new_entry));
+					if (ino == -1) {
+						is_long = 0;
+						get_new_entry = 0;
+						break;
+					}
+					ds = (struct msdos_dir_slot *) de;
+				}
+				PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+			}
+			PRINTK(("Long filename: %s, get_new_entry: %d\n", longname, get_new_entry));
+		} else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+			char bufname[13];
+			char *ptname = bufname;
+			int dotoffset = 0;
+
+			if (is_long) {
+				unsigned char sum;
+
+				for (sum = i = 0; i < 11; i++) {
+					sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+				}
+
+				if (sum != alias_checksum) {
+					PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
+					is_long = 0;
+				}
+			}
+
+			if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->dotsOK) {
+				bufname[0] = '.';
+				dotoffset = 1;
+				ptname = bufname+1;
+			}
+			for (i = last = 0; i < 8; i++) {
+				if (!(c = de->name[i])) break;
+				if (c >= 'A' && c <= 'Z') c += 32;
+				/* see namei.c, msdos_format_name */
+				if (c == 0x05) c = 0xE5;
+				if (c != ' ')
+					last = i+1;
+				ptname[i] = c;
+			}
+			i = last;
+			ptname[i] = '.';
+			i++;
+			for (i2 = 0; i2 < 3; i2++) {
+				if (!(c = de->ext[i2])) break;
+				if (c >= 'A' && c <= 'Z') c += 32;
+				if (c != ' ')
+					last = i+1;
+				ptname[i] = c;
+				i++;
+			}
+			if ((i = last) != 0) {
+				if (!strcmp(de->name,MSDOS_DOT))
+					ino = inode->i_ino;
+				else if (!strcmp(de->name,MSDOS_DOTDOT))
+					ino = fat_parent_ino(inode,0);
+
+				if (!is_long) {
+					if (filldir(dirent, bufname, i+dotoffset, oldpos, ino) < 0) {
+						filp->f_pos = oldpos;
+						break;
+					}
+				} else {
+					if (filldir(dirent, longname, long_len, oldpos, ino) < 0) {
+						filp->f_pos = oldpos;
+						break;
+					}
+				}
+				oldpos = filp->f_pos;
+			}
+			is_long = 0;
+		} else {
+			is_long = 0;
+			oldpos = filp->f_pos;
+		}
+		ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);	
+	}
+	if (bh) brelse(bh);
+	return 0;
+}
diff -u --recursive --new-file v1.3.59/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v1.3.59/linux/fs/fat/fatfs_syms.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/fatfs_syms.c	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,46 @@
+#include <linux/module.h>
+
+#include <linux/msdos_fs.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+static struct symbol_table fat_syms = {
+#include <linux/symtab_begin.h>
+	X(fat_a2alias),
+	X(fat_a2uni),
+	X(fat_add_cluster),
+	X(fat_bmap),
+	X(fat_brelse),
+	X(fat_cache_inval_inode),
+	X(fat_date_unix2dos),
+	X(fat_dir_operations),
+	X(fat_file_read),
+	X(fat_file_write),
+	X(fat_fs_panic),
+	X(fat_get_entry),
+	X(fat_lock_creation),
+	X(fat_mark_buffer_dirty),
+	X(fat_mmap),
+	X(fat_notify_change),
+	X(fat_parent_ino),
+	X(fat_put_inode),
+	X(fat_put_super),
+	X(fat_read_inode),
+	X(fat_read_super),
+	X(fat_readdir),
+	X(fat_scan),
+	X(fat_smap),
+	X(fat_statfs),
+	X(fat_truncate),
+	X(fat_uni2asc_pg),
+	X(fat_unlock_creation),
+	X(fat_write_inode),
+#include <linux/symtab_end.h>
+};                                           
+
+int init_fat_fs(void)
+{
+	return register_symtab(&fat_syms);
+}
+
diff -u --recursive --new-file v1.3.59/linux/fs/fat/file.c linux/fs/fat/file.c
--- v1.3.59/linux/fs/fat/file.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/file.c	Wed Feb  7 09:39:27 1996
@@ -0,0 +1,375 @@
+/*
+ *  linux/fs/msdos/file.c
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ *
+ *  MS-DOS regular file handling primitives
+ */
+
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include "msbuffer.h"
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+static struct file_operations fat_file_operations = {
+	NULL,			/* lseek - default */
+	fat_file_read,		/* read */
+	fat_file_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	generic_mmap,		/* mmap */
+	NULL,			/* no special open is needed */
+	NULL,			/* release */
+	file_fsync		/* fsync */
+};
+
+struct inode_operations fat_file_inode_operations = {
+	&fat_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 */
+	fat_bmap,		/* bmap */
+	fat_truncate,		/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+/* #Specification: msdos / special devices / mmap	
+	Mmapping does work because a special mmap is provide in that case.
+	Note that it is much less efficient than the generic_mmap normally
+	used since it allocate extra buffer. generic_mmap is used for
+	normal device (512 bytes hardware sectors).
+*/
+static struct file_operations fat_file_operations_1024 = {
+	NULL,			/* lseek - default */
+	fat_file_read,		/* read */
+	fat_file_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	fat_mmap,		/* mmap */
+	NULL,			/* no special open is needed */
+	NULL,			/* release */
+	file_fsync		/* fsync */
+};
+
+/* #Specification: msdos / special devices / swap file
+	Swap file can't work on special devices with a large sector
+	size (1024 bytes hard sector). Those devices have a weird
+	MsDOS filesystem layout. Generally a single hardware sector
+	may contain 2 unrelated logical sector. This mean that there is
+	no easy way to do a mapping between disk sector of a file and virtual
+	memory. So swap file is difficult (not available right now)
+	on those devices. Off course, Ext2 does not have this problem.
+*/
+struct inode_operations fat_file_inode_operations_1024 = {
+	&fat_file_operations_1024,	/* 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,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	fat_truncate,		/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+#define MSDOS_PREFETCH	32
+struct fat_pre {
+	int file_sector;/* Next sector to read in the prefetch table */
+			/* This is relative to the file, not the disk */
+	struct buffer_head *bhlist[MSDOS_PREFETCH];	/* All buffers needed */
+	int nblist;	/* Number of buffers in bhlist */
+	int nolist;	/* index in bhlist */
+};
+/*
+	Order the prefetch of more sectors.
+*/
+static void fat_prefetch (
+	struct inode *inode,
+	struct fat_pre *pre,
+	int nb)		/* How many must be prefetch at once */
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bhreq[MSDOS_PREFETCH];	/* Buffers not */
+												/* already read */
+	int nbreq=0;			/* Number of buffers in bhreq */
+	int i;
+	for (i=0; i<nb; i++){
+		int sector = fat_smap(inode,pre->file_sector);
+		if (sector != 0){
+			struct buffer_head *bh;
+			PRINTK (("fsector2 %d -> %d\n",pre->file_sector-1,sector));
+			pre->file_sector++;
+			bh = getblk(inode->i_dev,sector,SECTOR_SIZE);
+			if (bh == NULL)	break;
+			pre->bhlist[pre->nblist++] = bh;
+			if (!fat_is_uptodate(sb,bh)) bhreq[nbreq++] = bh;
+		}else{
+			break;
+		}
+	}
+	if (nbreq > 0) fat_ll_rw_block (sb,READ,nbreq,bhreq);
+	for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL;
+}
+
+/*
+	Read a file into user space
+*/
+int fat_file_read(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+	int count)
+{
+	struct super_block *sb = inode->i_sb;
+	char *start = buf;
+	char *end   = buf + count;
+	int i;
+	int left_in_file;
+	struct fat_pre pre;
+		
+
+	if (!inode) {
+		printk("fat_file_read: inode = NULL\n");
+		return -EINVAL;
+	}
+	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+		printk("fat_file_read: mode = %07o\n",inode->i_mode);
+		return -EINVAL;
+	}
+	if (filp->f_pos >= inode->i_size || count <= 0) return 0;
+	/*
+		Tell the buffer cache which block we expect to read in advance
+		Since we are limited with the stack, we preread only MSDOS_PREFETCH
+		because we have to keep the result into the local
+		arrays pre.bhlist and bhreq.
+		
+		Each time we process one block in bhlist, we replace
+		it by a new prefetch block if needed.
+	*/
+	PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,filp->f_pos,inode->i_size,count));
+	{
+		/*
+			We must prefetch complete block, so we must
+			take in account the offset in the first block.
+		*/
+		int count_max = (filp->f_pos & (SECTOR_SIZE-1)) + count;
+		int   to_reada;	/* How many block to read all at once */
+		pre.file_sector = filp->f_pos >> SECTOR_BITS;
+		to_reada = count_max / SECTOR_SIZE;
+		if (count_max & (SECTOR_SIZE-1)) to_reada++;
+		if (filp->f_reada || !MSDOS_I(inode)->i_binary){
+			/* Doing a read ahead on ascii file make sure we always */
+			/* pre read enough, since we don't know how many blocks */
+			/* we really need */
+			int ahead = read_ahead[MAJOR(inode->i_dev)];
+			PRINTK (("to_reada %d ahead %d\n",to_reada,ahead));
+			if (ahead == 0) ahead = 8;
+			to_reada += ahead;
+		}
+		if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH;
+		pre.nblist = 0;
+		fat_prefetch (inode,&pre,to_reada);
+	}
+	pre.nolist = 0;
+	PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist));
+	while ((left_in_file = inode->i_size - filp->f_pos) > 0
+		&& buf < end){
+		struct buffer_head *bh = pre.bhlist[pre.nolist];
+		char *data;
+		int size,offset;
+		if (bh == NULL) break;
+		pre.bhlist[pre.nolist] = NULL;
+		pre.nolist++;
+		if (pre.nolist == MSDOS_PREFETCH/2){
+			memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2
+				,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0]));
+			pre.nblist -= MSDOS_PREFETCH/2;
+			fat_prefetch (inode,&pre,MSDOS_PREFETCH/2);
+			pre.nolist = 0;
+		}
+		PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count));
+		wait_on_buffer(bh);
+		if (!fat_is_uptodate(sb,bh)){
+			/* read error  ? */
+			brelse (bh);
+			break;
+		}
+		offset = filp->f_pos & (SECTOR_SIZE-1);
+		data = bh->b_data + offset;
+		size = MIN(SECTOR_SIZE-offset,left_in_file);
+		if (MSDOS_I(inode)->i_binary) {
+			size = MIN(size,end-buf);
+			memcpy_tofs(buf,data,size);
+			buf += size;
+			filp->f_pos += size;
+		}else{
+			for (; size && buf < end; size--) {
+				char ch = *data++;
+				filp->f_pos++;
+				if (ch == 26){
+					filp->f_pos = inode->i_size;
+					break;
+				}else if (ch != '\r'){
+					put_user(ch,buf++);
+				}
+			}
+		}
+		brelse(bh);
+	}
+	PRINTK (("--- %d -> %d\n",count,(int)(buf-start)));
+	for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]);
+	if (start == buf) return -EIO;
+	if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME;
+	filp->f_reada = 1;	/* Will be reset if a lseek is done */
+	return buf-start;
+}
+
+/*
+	Write to a file either from user space
+*/
+int fat_file_write(
+	struct inode *inode,
+	struct file *filp,
+	const char *buf,
+	int count)
+{
+	struct super_block *sb = inode->i_sb;
+	int sector,offset,size,left,written;
+	int error,carry;
+	const char *start;
+	char *to,ch;
+	struct buffer_head *bh;
+	int binary_mode = MSDOS_I(inode)->i_binary;
+
+	if (!inode) {
+		printk("fat_file_write: inode = NULL\n");
+		return -EINVAL;
+	}
+	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+		printk("fat_file_write: mode = %07o\n",inode->i_mode);
+		return -EINVAL;
+	}
+	/* system files are immutable */
+	if (IS_IMMUTABLE(inode)) return -EPERM;
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+	if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
+	if (count <= 0) return 0;
+	error = carry = 0;
+	for (start = buf; count || carry; count -= size) {
+		while (!(sector = fat_smap(inode,filp->f_pos >> SECTOR_BITS)))
+			if ((error = fat_add_cluster(inode)) < 0) break;
+		if (error) {
+			fat_truncate(inode);
+			break;
+		}
+		offset = filp->f_pos & (SECTOR_SIZE-1);
+		size = MIN(SECTOR_SIZE-offset,MAX(carry,count));
+		if (binary_mode
+			&& offset == 0
+			&& (size == SECTOR_SIZE
+				|| filp->f_pos + size >= inode->i_size)){
+			/* No need to read the block first since we will */
+			/* completely overwrite it */
+			/* or at least write past the end of file */
+			if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){
+				error = -EIO;
+				break;
+			}
+		}else if (!(bh = bread(inode->i_dev,sector,SECTOR_SIZE))) {
+			error = -EIO;
+			break;
+		}
+		if (binary_mode) {
+			memcpy_fromfs(bh->b_data+offset,buf,written = size);
+			buf += size;
+		}
+		else {
+			written = left = SECTOR_SIZE-offset;
+			to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
+			if (carry) {
+				*to++ = '\n';
+				left--;
+				carry = 0;
+			}
+			for (size = 0; size < count && left; size++) {
+				if ((ch = get_user(buf++)) == '\n') {
+					*to++ = '\r';
+					left--;
+				}
+				if (!left) carry = 1;
+				else {
+					*to++ = ch;
+					left--;
+				}
+			}
+			written -= left;
+		}
+		filp->f_pos += written;
+		if (filp->f_pos > inode->i_size) {
+			inode->i_size = filp->f_pos;
+			inode->i_dirt = 1;
+		}
+		fat_set_uptodate(sb,bh,1);
+		mark_buffer_dirty(bh, 0);
+		brelse(bh);
+	}
+	if (start == buf)
+		return error;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+	inode->i_dirt = 1;
+	return buf-start;
+}
+
+void fat_truncate(struct inode *inode)
+{
+	int cluster;
+
+	/* Why no return value?  Surely the disk could fail... */
+	if (IS_IMMUTABLE(inode)) return /* -EPERM */;
+	cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
+	(void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
+	MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+	inode->i_dirt = 1;
+}
diff -u --recursive --new-file v1.3.59/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v1.3.59/linux/fs/fat/inode.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/inode.c	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,542 @@
+/*
+ *  linux/fs/msdos/inode.c
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+#include <asm/segment.h>
+
+
+
+void fat_put_inode(struct inode *inode)
+{
+	struct inode *depend;
+	struct super_block *sb;
+
+	if (inode->i_nlink) {
+		if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
+		return;
+	}
+	inode->i_size = 0;
+	fat_truncate(inode);
+	depend = MSDOS_I(inode)->i_depend;
+	sb = inode->i_sb;
+	clear_inode(inode);
+	if (depend) {
+		if (MSDOS_I(depend)->i_old != inode) {
+			printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+			    depend, inode, MSDOS_I(depend)->i_old);
+			fat_fs_panic(sb,"...");
+			return;
+		}
+		MSDOS_I(depend)->i_old = NULL;
+		iput(depend);
+	}
+}
+
+
+void fat_put_super(struct super_block *sb)
+{
+	fat_cache_inval_dev(sb->s_dev);
+	set_blocksize (sb->s_dev,BLOCK_SIZE);
+	lock_super(sb);
+	sb->s_dev = 0;
+	unlock_super(sb);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+
+static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
+    gid_t *gid,int *umask,int *debug,int *fat,int *quiet,
+	int *blksize, char *dotsOK, char *sys_immutable)
+{
+	char *this_char,*value;
+
+	*check = 'n';
+	*conversion = 'b';
+	*dotsOK = 0;
+	*uid = current->uid;
+	*gid = current->gid;
+	*umask = current->fs->umask;
+	*debug = *fat = *quiet = *sys_immutable = 0;
+	if (!options) return 1;
+	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+		if ((value = strchr(this_char,'=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char,"check") && value) {
+			if (value[0] && !value[1] && strchr("rns",*value))
+				*check = *value;
+			else if (!strcmp(value,"relaxed")) *check = 'r';
+			else if (!strcmp(value,"normal")) *check = 'n';
+			else if (!strcmp(value,"strict")) *check = 's';
+			else return 0;
+		}
+		else if (!strcmp(this_char,"conv") && value) {
+			if (value[0] && !value[1] && strchr("bta",*value))
+				*conversion = *value;
+			else if (!strcmp(value,"binary")) *conversion = 'b';
+			else if (!strcmp(value,"text")) *conversion = 't';
+			else if (!strcmp(value,"auto")) *conversion = 'a';
+			else return 0;
+		}
+		else if (!strcmp(this_char,"dots")) {
+		  *dotsOK = 1;
+		}
+		else if (!strcmp(this_char,"nodots")) {
+		  *dotsOK = 0;
+		}
+		else if (!strcmp(this_char,"dotsOK") && value) {
+			if (!strcmp(value,"yes")) *dotsOK = 1;
+			else if (!strcmp(value,"no")) *dotsOK = 0;
+			else return 0;
+		}
+		else if (!strcmp(this_char,"uid")) {
+			if (!value || !*value)
+				return 0;
+			*uid = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		}
+		else if (!strcmp(this_char,"gid")) {
+			if (!value || !*value)
+				return 0;
+			*gid = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		}
+		else if (!strcmp(this_char,"umask")) {
+			if (!value || !*value)
+				return 0;
+			*umask = simple_strtoul(value,&value,8);
+			if (*value)
+				return 0;
+		}
+		else if (!strcmp(this_char,"debug")) {
+			if (value) return 0;
+			*debug = 1;
+		}
+		else if (!strcmp(this_char,"fat")) {
+			if (!value || !*value)
+				return 0;
+			*fat = simple_strtoul(value,&value,0);
+			if (*value || (*fat != 12 && *fat != 16))
+				return 0;
+		}
+		else if (!strcmp(this_char,"quiet")) {
+			if (value) return 0;
+			*quiet = 1;
+		}
+		else if (!strcmp(this_char,"blocksize")) {
+			*blksize = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+			if (*blksize != 512 && *blksize != 1024){
+				printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
+			}
+		}
+		else if (!strcmp(this_char,"sys_immutable")) {
+			if (value)
+				return 0;
+			*sys_immutable = 1;
+		}
+		else return 1;
+	}
+	return 1;
+}
+
+
+/* Read the super block of an MS-DOS FS. */
+
+struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
+{
+	struct buffer_head *bh;
+	struct msdos_boot_sector *b;
+	int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+	int debug,error,fat,quiet;
+	char check,conversion,dotsOK,sys_immutable;
+	uid_t uid;
+	gid_t gid;
+	int umask;
+	int blksize = 512;
+
+	MOD_INC_USE_COUNT;
+	if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
+		blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)];
+		if (blksize != 512){
+			printk ("MSDOS: Hardware sector size is %d\n",blksize);
+		}
+	}
+	if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask,
+	    &debug,&fat,&quiet,&blksize,&dotsOK,&sys_immutable)
+		|| (blksize != 512 && blksize != 1024)) {
+		sb->s_dev = 0;
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	cache_init();
+	lock_super(sb);
+	/* The first read is always 1024 bytes */
+	sb->s_blocksize = 1024;
+	set_blocksize(sb->s_dev, 1024);
+	bh = bread(sb->s_dev, 0, 1024);
+	unlock_super(sb);
+	if (bh == NULL || !fat_is_uptodate(sb,bh)) {
+		brelse (bh);
+		sb->s_dev = 0;
+		printk("FAT bread failed\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	b = (struct msdos_boot_sector *) bh->b_data;
+	set_blocksize(sb->s_dev, blksize);
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.  
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ * 
+ * DOS 3 partition managers got around this problem by faking a 
+ * larger sector size, ie treating multiple physical sectors as 
+ * a single logical sector.
+ * 
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+    /* don't divide by zero */
+
+	logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size);
+	sector_mult = logical_sector_size >> SECTOR_BITS;
+	MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
+	MSDOS_SB(sb)->fats = b->fats;
+	MSDOS_SB(sb)->fat_start = CF_LE_W(b->reserved)*sector_mult;
+	MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
+	MSDOS_SB(sb)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W(
+	    b->fat_length))*sector_mult;
+	MSDOS_SB(sb)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries
+	    ));
+	MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
+	    MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
+	    sector_mult);
+	data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ?
+	    CF_LE_W(*((unsigned short *) &b->sectors)) :
+	    CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
+	error = !b->cluster_size || !sector_mult;
+	if (!error) {
+		MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
+		    b->cluster_size/sector_mult : 0;
+		MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
+		    MSDOS_FAT12 ? 16 : 12;
+		fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+		    MSDOS_SB(sb)->fat_bits;
+		error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
+		    (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+		    MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+		    || !b->secs_track || !b->heads;
+	}
+	brelse(bh);
+	/*
+		This must be done after the brelse because the bh is a dummy
+		allocated by fat_bread (see buffer.c)
+	*/
+	sb->s_blocksize = blksize;	/* Using this small block size solve the */
+				/* the misfit with buffer cache and cluster */
+				/* because cluster (DOS) are often aligned */
+				/* on odd sector */
+	sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+	if (error || debug) {
+		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+		    "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(sb)->fat_bits,check,
+		    conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(sb)) ?
+		    ",bmap" : "");
+		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
+		    "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
+		    MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->
+		    fat_length,MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
+		    MSDOS_SB(sb)->data_start,CF_LE_W(*(unsigned short *) &b->
+		    sectors),(unsigned long)b->total_sect,logical_sector_size);
+		printk ("Transaction block size = %d\n",blksize);
+	}
+	if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+		MSDOS_SB(sb)->clusters = fat_clusters-2;
+	if (error) {
+		if (!silent)
+			printk("VFS: Can't find a valid MSDOS filesystem on dev "
+			       "%s.\n", kdevname(sb->s_dev));
+		sb->s_dev = 0;
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	sb->s_magic = MSDOS_SUPER_MAGIC;
+	MSDOS_SB(sb)->name_check = check;
+	MSDOS_SB(sb)->conversion = conversion;
+	/* set up enough so that it can read an inode */
+	MSDOS_SB(sb)->fs_uid = uid;
+	MSDOS_SB(sb)->fs_gid = gid;
+	MSDOS_SB(sb)->fs_umask = umask;
+	MSDOS_SB(sb)->quiet = quiet;
+	MSDOS_SB(sb)->dotsOK = dotsOK;
+	MSDOS_SB(sb)->sys_immutable = sys_immutable;
+	MSDOS_SB(sb)->vfat = 0;   /* vfat_read_super sets this */
+	MSDOS_SB(sb)->umsdos = 0; /* umsdos_read_super will set this */
+	MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
+	MSDOS_SB(sb)->fat_wait = NULL;
+	MSDOS_SB(sb)->fat_lock = 0;
+	MSDOS_SB(sb)->prev_free = 0;
+	if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
+		sb->s_dev = 0;
+		printk("get root inode failed\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	return sb;
+}
+
+
+void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
+{
+	int free,nr;
+	struct statfs tmp;
+
+	lock_fat(sb);
+	if (MSDOS_SB(sb)->free_clusters != -1)
+		free = MSDOS_SB(sb)->free_clusters;
+	else {
+		free = 0;
+		for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
+			if (!fat_access(sb,nr,-1)) free++;
+		MSDOS_SB(sb)->free_clusters = free;
+	}
+	unlock_fat(sb);
+	tmp.f_type = sb->s_magic;
+	tmp.f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
+	tmp.f_blocks = MSDOS_SB(sb)->clusters;
+	tmp.f_bfree = free;
+	tmp.f_bavail = free;
+	tmp.f_files = 0;
+	tmp.f_ffree = 0;
+	tmp.f_namelen = 12;
+	memcpy_tofs(buf, &tmp, bufsiz);
+}
+
+
+int fat_bmap(struct inode *inode,int block)
+{
+	struct msdos_sb_info *sb;
+	int cluster,offset;
+
+	sb = MSDOS_SB(inode->i_sb);
+	if (inode->i_ino == MSDOS_ROOT_INO) {
+		return sb->dir_start + block;
+	}
+	cluster = block/sb->cluster_size;
+	offset = block % sb->cluster_size;
+	if (!(cluster = get_cluster(inode,cluster))) return 0;
+	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
+}
+
+
+void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *raw_entry;
+	int nr;
+
+/* printk("read inode %d\n",inode->i_ino); */
+	MSDOS_I(inode)->i_busy = 0;
+	MSDOS_I(inode)->i_depend = MSDOS_I(inode)->i_old = NULL;
+	MSDOS_I(inode)->i_binary = 1;
+	inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid;
+	inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid;
+	if (inode->i_ino == MSDOS_ROOT_INO) {
+		inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+		    S_IFDIR;
+		inode->i_op = fs_dir_inode_ops;
+		inode->i_nlink = fat_subdirs(inode)+2;
+		    /* subdirs (neither . nor ..) plus . and "self" */
+		inode->i_size = MSDOS_SB(inode->i_sb)->dir_entries*
+		    sizeof(struct msdos_dir_entry);
+		inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*
+		    SECTOR_SIZE;
+		inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+		    inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
+		MSDOS_I(inode)->i_start = 0;
+		MSDOS_I(inode)->i_attrs = 0;
+		inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+		return;
+	}
+	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
+	    SECTOR_SIZE))) {
+		printk("dev = %s, ino = %ld\n",
+		       kdevname(inode->i_dev), inode->i_ino);
+		panic("fat_read_inode: unable to read i-node block");
+	}
+	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+	    [inode->i_ino & (MSDOS_DPB-1)];
+	if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
+		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
+		    ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR;
+		inode->i_op = fs_dir_inode_ops;
+
+		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+		inode->i_nlink = fat_subdirs(inode);
+		    /* includes .., compensating for "self" */
+#ifdef DEBUG
+		if (!inode->i_nlink) {
+			printk("directory %d: i_nlink == 0\n",inode->i_ino);
+			inode->i_nlink = 1;
+		}
+#endif
+		inode->i_size = 0;
+		if ((nr = CF_LE_W(raw_entry->start)) != 0)
+			while (nr != -1) {
+				inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->
+				    i_sb)->cluster_size;
+				if (!(nr = fat_access(inode->i_sb,nr,-1))) {
+					printk("Directory %ld: bad FAT\n",
+					    inode->i_ino);
+					break;
+				}
+			}
+	} else { /* not a directory */
+		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
+		    (IS_NOEXEC(inode) ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+		    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG;
+		inode->i_op = (sb->s_blocksize == 1024)
+			? &fat_file_inode_operations_1024
+			: &fat_file_inode_operations;
+		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+		inode->i_nlink = 1;
+		inode->i_size = CF_LE_L(raw_entry->size);
+	}
+	if(raw_entry->attr & ATTR_SYS)
+		if (MSDOS_I(inode)->sys_immutable)
+			inode->i_flags |= S_IMMUTABLE;
+	MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(inode->i_sb)->conversion,
+	    raw_entry->ext);
+	MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
+	/* this is as close to the truth as we can get ... */
+	inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*SECTOR_SIZE;
+	inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+	    inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
+	inode->i_mtime = inode->i_atime =
+	    date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
+	inode->i_ctime =
+		MSDOS_SB(inode->i_sb)->vfat
+		? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
+		: inode->i_mtime;
+	brelse(bh);
+}
+
+
+void fat_write_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *raw_entry;
+
+	inode->i_dirt = 0;
+	if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
+	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
+	    SECTOR_SIZE))) {
+		printk("dev = %s, ino = %ld\n",
+		       kdevname(inode->i_dev), inode->i_ino);
+		panic("msdos_write_inode: unable to read i-node block");
+	}
+	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+	    [inode->i_ino & (MSDOS_DPB-1)];
+	if (S_ISDIR(inode->i_mode)) {
+		raw_entry->attr = ATTR_DIR;
+		raw_entry->size = 0;
+	}
+	else {
+		raw_entry->attr = ATTR_NONE;
+		raw_entry->size = CT_LE_L(inode->i_size);
+	}
+	raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
+	    MSDOS_I(inode)->i_attrs;
+	raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start);
+	fat_date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
+	raw_entry->time = CT_LE_W(raw_entry->time);
+	raw_entry->date = CT_LE_W(raw_entry->date);
+	if (MSDOS_SB(sb)->vfat) {
+		fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
+		raw_entry->ctime = CT_LE_W(raw_entry->ctime);
+		raw_entry->cdate = CT_LE_W(raw_entry->cdate);
+	}
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+}
+
+
+int fat_notify_change(struct inode * inode,struct iattr * attr)
+{
+	int error;
+
+	error = inode_change_ok(inode, attr);
+	if (error)
+		return error;
+
+	if (((attr->ia_valid & ATTR_UID) && 
+	     (attr->ia_uid != MSDOS_SB(inode->i_sb)->fs_uid)) ||
+	    ((attr->ia_valid & ATTR_GID) && 
+	     (attr->ia_gid != MSDOS_SB(inode->i_sb)->fs_gid)) ||
+	    ((attr->ia_valid & ATTR_MODE) &&
+	     (attr->ia_mode & ~MSDOS_VALID_MODE)))
+		error = -EPERM;
+
+	if (error)
+		return MSDOS_SB(inode->i_sb)->quiet ? 0 : error;
+
+	inode_setattr(inode, attr);
+
+	if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
+		inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
+	else
+		inode->i_mode |= S_IXUGO;
+
+	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
+	    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
+	    ~MSDOS_SB(inode->i_sb)->fs_umask;
+	return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_fat_fs();
+}
+
+
+void cleanup_module(void)
+{
+	/* Nothing to be done, really! */
+	return;
+}
+#endif
+
diff -u --recursive --new-file v1.3.59/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v1.3.59/linux/fs/fat/misc.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/misc.c	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,553 @@
+/*
+ *  linux/fs/msdos/misc.c
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include "msbuffer.h"
+
+#define PRINTK(x)
+#define Printk(x)	printk x
+
+/* Well-known binary file extensions - of course there are many more */
+
+static char bin_extensions[] =
+  "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */
+  "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z  " "ARJ"	/* common archivers */
+  "TZ " "TAZ" "TZP" "TPZ"		/* abbreviations of tar.Z and tar.zip */
+  "GZ " "TGZ" "DEB"			/* .gz, .tar.gz and Debian packages   */
+  "GIF" "BMP" "TIF" "GL " "JPG" "PCX"	/* graphics */
+  "TFM" "VF " "GF " "PK " "PXL" "DVI";	/* TeX */
+
+
+/*
+ * fat_fs_panic reports a severe file system problem and sets the file system
+ * read-only. The file system can be made writable again by remounting it.
+ */
+
+void fat_fs_panic(struct super_block *s,const char *msg)
+{
+	int not_ro;
+
+	not_ro = !(s->s_flags & MS_RDONLY);
+	if (not_ro) s->s_flags |= MS_RDONLY;
+	printk("Filesystem panic (dev %s, ", kdevname(s->s_dev));
+	printk("mounted on %s:%ld)\n  %s\n", /* note: kdevname returns & static char[] */
+	       kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg);
+	if (not_ro)
+		printk("  File system has been set read-only\n");
+}
+
+
+/*
+ * is_binary selects optional text conversion based on the conversion mode and
+ * the extension part of the file name.
+ */
+
+int is_binary(char conversion,char *extension)
+{
+	char *walk;
+
+	switch (conversion) {
+		case 'b':
+			return 1;
+		case 't':
+			return 0;
+		case 'a':
+			for (walk = bin_extensions; *walk; walk += 3)
+				if (!strncmp(extension,walk,3)) return 1;
+			return 0;
+		default:
+			printk("Invalid conversion mode - defaulting to "
+			    "binary.\n");
+			return 1;
+	}
+}
+
+
+/* File creation lock. This is system-wide to avoid deadlocks in rename. */
+/* (rename might deadlock before detecting cross-FS moves.) */
+
+static struct wait_queue *creation_wait = NULL;
+static creation_lock = 0;
+
+
+void fat_lock_creation(void)
+{
+	while (creation_lock) sleep_on(&creation_wait);
+	creation_lock = 1;
+}
+
+
+void fat_unlock_creation(void)
+{
+	creation_lock = 0;
+	wake_up(&creation_wait);
+}
+
+
+void lock_fat(struct super_block *sb)
+{
+	while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait);
+	MSDOS_SB(sb)->fat_lock = 1;
+}
+
+
+void unlock_fat(struct super_block *sb)
+{
+	MSDOS_SB(sb)->fat_lock = 0;
+	wake_up(&MSDOS_SB(sb)->fat_wait);
+}
+
+
+/*
+ * fat_add_cluster tries to allocate a new cluster and adds it to the file
+ * represented by inode. The cluster is zero-initialized.
+ */
+
+int fat_add_cluster(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	int count,nr,limit,last,curr,sector,last_sector,file_cluster;
+	struct buffer_head *bh;
+	int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size;
+
+	if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+	if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC;
+	lock_fat(inode->i_sb);
+	limit = MSDOS_SB(inode->i_sb)->clusters;
+	nr = limit; /* to keep GCC happy */
+	for (count = 0; count < limit; count++) {
+		nr = ((count+MSDOS_SB(inode->i_sb)->prev_free) % limit)+2;
+		if (fat_access(inode->i_sb,nr,-1) == 0) break;
+	}
+	PRINTK (("cnt = %d --",count));
+#ifdef DEBUG
+printk("free cluster: %d\n",nr);
+#endif
+	MSDOS_SB(inode->i_sb)->prev_free = (count+MSDOS_SB(inode->i_sb)->
+	    prev_free+1) % limit;
+	if (count >= limit) {
+		MSDOS_SB(inode->i_sb)->free_clusters = 0;
+		unlock_fat(inode->i_sb);
+		return -ENOSPC;
+	}
+	fat_access(inode->i_sb,nr,MSDOS_SB(inode->i_sb)->fat_bits == 12 ?
+	    0xff8 : 0xfff8);
+	if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
+		MSDOS_SB(inode->i_sb)->free_clusters--;
+	unlock_fat(inode->i_sb);
+#ifdef DEBUG
+printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
+#endif
+	last = 0;
+	/* We must locate the last cluster of the file to add this
+	   new one (nr) to the end of the link list (the FAT).
+	   
+	   Here file_cluster will be the number of the last cluster of the
+	   file (before we add nr).
+	   
+	   last is the corresponding cluster number on the disk. We will
+	   use last to plug the nr cluster. We will use file_cluster to
+	   update the cache.
+	*/
+	file_cluster = 0;
+	if ((curr = MSDOS_I(inode)->i_start) != 0) {
+		cache_lookup(inode,INT_MAX,&last,&curr);
+		file_cluster = last;
+		while (curr && curr != -1){
+			PRINTK (("."));
+			file_cluster++;
+			if (!(curr = fat_access(inode->i_sb,
+			    last = curr,-1))) {
+				fat_fs_panic(inode->i_sb,"File without EOF");
+				return -ENOSPC;
+			}
+		}
+		PRINTK ((" --  "));
+	}
+#ifdef DEBUG
+printk("last = %d\n",last);
+#endif
+	if (last) fat_access(inode->i_sb,last,nr);
+	else {
+		MSDOS_I(inode)->i_start = nr;
+		inode->i_dirt = 1;
+	}
+#ifdef DEBUG
+if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
+#endif
+	sector = MSDOS_SB(inode->i_sb)->data_start+(nr-2)*cluster_size;
+	last_sector = sector + cluster_size;
+	for ( ; sector < last_sector; sector++) {
+		#ifdef DEBUG
+			printk("zeroing sector %d\n",sector);
+		#endif
+		if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE)))
+			printk("getblk failed\n");
+		else {
+			memset(bh->b_data,0,SECTOR_SIZE);
+			fat_set_uptodate(sb,bh,1);
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+		}
+	}
+	if (file_cluster != inode->i_blocks/cluster_size){
+		printk ("file_cluster badly computed!!! %d <> %ld\n"
+			,file_cluster,inode->i_blocks/cluster_size);
+	}else{
+		cache_add(inode,file_cluster,nr);
+	}
+	inode->i_blocks += cluster_size;
+	if (S_ISDIR(inode->i_mode)) {
+		if (inode->i_size & (SECTOR_SIZE-1)) {
+			fat_fs_panic(inode->i_sb,"Odd directory size");
+			inode->i_size = (inode->i_size+SECTOR_SIZE) &
+			    ~(SECTOR_SIZE-1);
+		}
+		inode->i_size += SECTOR_SIZE*cluster_size;
+#ifdef DEBUG
+printk("size is %d now (%x)\n",inode->i_size,inode);
+#endif
+		inode->i_dirt = 1;
+	}
+	return 0;
+}
+
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+int date_dos2unix(unsigned short time,unsigned short date)
+{
+	int month,year,secs;
+
+	month = ((date >> 5) & 15)-1;
+	year = date >> 9;
+	secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+	    ((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 */
+	secs += sys_tz.tz_minuteswest*60;
+	return secs;
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+void fat_date_unix2dos(int unix_date,unsigned short *time,
+    unsigned short *date)
+{
+	int day,year,nl_day,month;
+
+	unix_date -= sys_tz.tz_minuteswest*60;
+	*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+	    (((unix_date/3600) % 24) << 11);
+	day = unix_date/86400-3652;
+	year = day/365;
+	if ((year+3)/4+365*year > day) year--;
+	day -= (year+3)/4+365*year;
+	if (day == 59 && !(year & 3)) {
+		nl_day = day;
+		month = 2;
+	}
+	else {
+		nl_day = (year & 3) || day <= 59 ? day : day-1;
+		for (month = 0; month < 12; month++)
+			if (day_n[month] > nl_day) break;
+	}
+	*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
+
+
+/* Returns the inode number of the directory entry at offset pos. If bh is
+   non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
+   returned in bh. */
+
+int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+    struct msdos_dir_entry **de)
+{
+	struct super_block *sb = dir->i_sb;
+	int sector,offset;
+
+	while (1) {
+		offset = *pos;
+		PRINTK (("get_entry offset %d\n",offset));
+		if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1)
+			return -1;
+		PRINTK (("get_entry sector %d %p\n",sector,*bh));
+		if (!sector)
+			return -1; /* beyond EOF */
+		*pos += sizeof(struct msdos_dir_entry);
+		if (*bh)
+			brelse(*bh);
+		PRINTK (("get_entry sector apres brelse\n"));
+		if (!(*bh = bread(dir->i_dev,sector,SECTOR_SIZE))) {
+			printk("Directory sread (sector %d) failed\n",sector);
+			continue;
+		}
+		PRINTK (("get_entry apres sread\n"));
+		*de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
+		    (SECTOR_SIZE-1)));
+		return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
+		    MSDOS_DIR_BITS);
+	}
+}
+
+
+/*
+ * Now an ugly part: this set of directory scan routines works on clusters
+ * rather than on inodes and sectors. They are necessary to locate the '..'
+ * directory "inode". raw_scan_sector operates in four modes:
+ *
+ * name     number   ino      action
+ * -------- -------- -------- -------------------------------------------------
+ * non-NULL -        X        Find an entry with that name
+ * NULL     non-NULL non-NULL Find an entry whose data starts at *number
+ * NULL     non-NULL NULL     Count subdirectories in *number. (*)
+ * NULL     NULL     non-NULL Find an empty entry
+ *
+ * (*) The return code should be ignored. It DOES NOT indicate success or
+ *     failure. *number has to be initialized to zero.
+ *
+ * - = not used, X = a value is returned unless NULL
+ *
+ * If res_bh is non-NULL, the buffer is not deallocated but returned to the
+ * caller on success. res_de is set accordingly.
+ *
+ * If cont is non-zero, raw_found continues with the entry after the one
+ * res_bh/res_de point to.
+ */
+
+
+#define RSS_NAME /* search for name */ \
+    done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
+     !(data[entry].attr & ATTR_VOLUME);
+
+#define RSS_START /* search for start cluster */ \
+    done = !IS_FREE(data[entry].name) && CF_LE_W(data[entry].start) == *number;
+
+#define RSS_FREE /* search for free entry */ \
+    { \
+	done = IS_FREE(data[entry].name); \
+	if (done) { \
+	    inode = iget(sb,sector*MSDOS_DPS+entry); \
+	    if (inode) { \
+	    /* Directory slots of busy deleted files aren't available yet. */ \
+		done = !MSDOS_I(inode)->i_busy; \
+		iput(inode); \
+	    } \
+	} \
+    }
+
+#define RSS_COUNT /* count subdirectories */ \
+    { \
+	done = 0; \
+	if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
+	    (*number)++; \
+    }
+
+static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
+    int *number,int *ino,struct buffer_head **res_bh,
+    struct msdos_dir_entry **res_de,char scantype)
+{
+	struct buffer_head *bh;
+	struct msdos_dir_entry *data;
+	struct inode *inode;
+	int entry,start,done;
+
+	if (!(bh = bread(sb->s_dev,sector,SECTOR_SIZE))) return -EIO;
+	data = (struct msdos_dir_entry *) bh->b_data;
+	for (entry = 0; entry < MSDOS_DPS; entry++) {
+/* RSS_COUNT:  if (data[entry].name == name) done=true else done=false. */
+		if (name) {
+			RSS_NAME
+			if (done && scantype) {   /* scantype != SCAN_ANY */
+				done = (data[entry].attr & ATTR_HIDDEN)
+					? (scantype==SCAN_HID)
+					: (scantype==SCAN_NOTHID);
+			}
+		} else {
+			if (!ino) RSS_COUNT
+			else {
+				if (number) RSS_START
+				else RSS_FREE
+			}
+		}
+		if (done) {
+			if (ino) *ino = sector*MSDOS_DPS+entry;
+			start = CF_LE_W(data[entry].start);
+			if (!res_bh) brelse(bh);
+			else {
+				*res_bh = bh;
+				*res_de = &data[entry];
+			}
+			return start;
+		}
+	}
+	brelse(bh);
+	return -ENOENT;
+}
+
+
+/*
+ * raw_scan_root performs raw_scan_sector on the root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
+    struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
+{
+	int count,cluster;
+
+	for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
+		if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
+		    name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
+	}
+	return -ENOENT;
+}
+
+
+/*
+ * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
+    int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
+    **res_de,char scantype)
+{
+	int count,cluster;
+
+#ifdef DEBUG
+	printk("raw_scan_nonroot: start=%d\n",start);
+#endif
+	do {
+		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
+			if ((cluster = raw_scan_sector(sb,(start-2)*
+			    MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
+			    count,name,number,ino,res_bh,res_de,scantype)) >= 0)
+				return cluster;
+		}
+		if (!(start = fat_access(sb,start,-1))) {
+			fat_fs_panic(sb,"FAT error");
+			break;
+		}
+#ifdef DEBUG
+	printk("next start: %d\n",start);
+#endif
+	}
+	while (start != -1);
+	return -ENOENT;
+}
+
+
+/*
+ * raw_scan performs raw_scan_sector on any sector.
+ *
+ * NOTE: raw_scan must not be used on a directory that is is the process of
+ *       being created.
+ */
+
+static int raw_scan(struct super_block *sb, int start, const char *name,
+    int *number, int *ino, struct buffer_head **res_bh,
+    struct msdos_dir_entry **res_de, char scantype)
+{
+	if (start) return raw_scan_nonroot
+		(sb,start,name,number,ino,res_bh,res_de,scantype);
+	else return raw_scan_root
+		(sb,name,number,ino,res_bh,res_de,scantype);
+}
+
+
+/*
+ * fat_parent_ino returns the inode number of the parent directory of dir.
+ * File creation has to be deferred while fat_parent_ino is running to
+ * prevent renames.
+ */
+
+int fat_parent_ino(struct inode *dir,int locked)
+{
+	static int zero = 0;
+	int error,curr,prev,nr;
+
+	if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
+	if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
+	if (!locked) fat_lock_creation(); /* prevent renames */
+	if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
+	    &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
+		if (!locked) fat_unlock_creation();
+		return curr;
+	}
+	if (!curr) nr = MSDOS_ROOT_INO;
+	else {
+		if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
+		    NULL,NULL,SCAN_ANY)) < 0) {
+			if (!locked) fat_unlock_creation();
+			return prev;
+		}
+		if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
+		    NULL,SCAN_ANY)) < 0) {
+			if (!locked) fat_unlock_creation();
+			return error;
+		}
+	}
+	if (!locked) fat_unlock_creation();
+	return nr;
+}
+
+
+/*
+ * fat_subdirs counts the number of sub-directories of dir. It can be run
+ * on directories being created.
+ */
+
+int fat_subdirs(struct inode *dir)
+{
+	int count;
+
+	count = 0;
+	if (dir->i_ino == MSDOS_ROOT_INO)
+		(void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+	else {
+		if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
+		else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
+		    NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+	}
+	return count;
+}
+
+
+/*
+ * Scans a directory for a given file (name points to its formatted name) or
+ * for an empty directory slot (name is NULL). Returns an error code or zero.
+ */
+
+int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
+    struct msdos_dir_entry **res_de,int *ino, char scantype)
+{
+	int res;
+
+	res = (name)
+		? raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+		    name, NULL, ino, res_bh, res_de, scantype)
+		: raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+		    NULL, NULL, ino, res_bh, res_de, scantype);
+	return res<0 ? res : 0;
+}
diff -u --recursive --new-file v1.3.59/linux/fs/fat/mmap.c linux/fs/fat/mmap.c
--- v1.3.59/linux/fs/fat/mmap.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/mmap.c	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,113 @@
+/*
+ *	fs/msdos/mmap.c
+ *
+ *	Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ *	Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+ *
+ *	msdos mmap handling
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/msdos_fs.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long fat_file_mmap_nopage(
+	struct vm_area_struct * area,
+	unsigned long address,
+	int error_code)
+{
+	struct inode * inode = area->vm_inode;
+	unsigned long page;
+	unsigned int clear;
+	int pos;
+	long gap;	/* distance from eof to pos */
+
+	page = __get_free_page(GFP_KERNEL);
+	if (!page)
+		return page;
+	address &= PAGE_MASK;
+	pos = address - area->vm_start + area->vm_offset;
+
+	clear = 0;
+	gap = inode->i_size - pos;
+	if (gap <= 0){
+		/* mmaping beyond end of file */
+		clear = PAGE_SIZE;
+	}else{
+		int cur_read;
+		int need_read;
+		struct file filp;
+		if (gap < PAGE_SIZE){
+			clear = PAGE_SIZE - gap;
+		}
+		filp.f_reada = 0;
+		filp.f_pos = pos;
+		need_read = PAGE_SIZE - clear;
+		{
+			unsigned long cur_fs = get_fs();
+			set_fs (KERNEL_DS);
+			cur_read = fat_file_read (inode,&filp,(char*)page
+				,need_read);
+			set_fs (cur_fs);
+		}
+		if (cur_read != need_read){
+			printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
+				,cur_read,need_read);
+		}
+	}
+	if (clear > 0){
+		memset ((char*)page+PAGE_SIZE-clear,0,clear);
+	}
+	return page;
+}
+
+struct vm_operations_struct fat_file_mmap = {
+	NULL,			/* open */
+	NULL,			/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
+	fat_file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	NULL,			/* swapout */
+	NULL,			/* swapin */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+	if (vma->vm_flags & VM_SHARED)	/* only PAGE_COW or read-only supported now */
+		return -EINVAL;
+	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+
+	vma->vm_inode = inode;
+	inode->i_count++;
+	vma->vm_ops = &fat_file_mmap;
+	return 0;
+}
+
+
diff -u --recursive --new-file v1.3.59/linux/fs/fat/msbuffer.h linux/fs/fat/msbuffer.h
--- v1.3.59/linux/fs/fat/msbuffer.h	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/msbuffer.h	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,34 @@
+
+struct buffer_head *fat_bread (struct super_block *sb, int block);
+struct buffer_head *fat_getblk (struct super_block *sb, int block);
+void fat_brelse (struct super_block *sb, struct buffer_head *bh);
+void fat_mark_buffer_dirty (struct super_block *sb,
+	 struct buffer_head *bh,
+	 int dirty_val);
+void fat_set_uptodate (struct super_block *sb,
+	 struct buffer_head *bh,
+	 int val);
+int fat_is_uptodate (struct super_block *sb, struct buffer_head *bh);
+void fat_ll_rw_block (struct super_block *sb, int opr,
+	int nbreq, struct buffer_head *bh[32]);
+
+/* These macros exist to avoid modifying all the code */
+/* They should be removed one day I guess */
+
+/* The versioning mechanism of the modules system define those macros */
+/* This remove some warnings */
+#ifdef brelse
+	#undef brelse
+#endif
+#ifdef bread
+	#undef bread
+#endif
+#ifdef getblk
+	#undef getblk
+#endif
+
+#define brelse(b)			fat_brelse(sb,b)
+#define bread(d,b,s)			fat_bread(sb,b)
+#define getblk(d,b,s)			fat_getblk(sb,b)
+#define mark_buffer_dirty(b,v)		fat_mark_buffer_dirty(sb,b,v)
+
diff -u --recursive --new-file v1.3.59/linux/fs/fat/tables.c linux/fs/fat/tables.c
--- v1.3.59/linux/fs/fat/tables.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/tables.c	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,232 @@
+/*
+ * linux/fs/msdos/tables.c
+ *
+ * ASCII / Unicode translation tables for VFAT filename handling.
+ * By Gordon Chaffee.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "tables.h"
+
+unsigned char page00[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, /* 0x98-0x9F */
+	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, /* 0xA0-0xA7 */
+	0xF9, 0xB8, 0x00, 0xAE, 0xAA, 0xF0, 0x00, 0xEE, /* 0xA8-0xAF */
+	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, /* 0xB0-0xB7 */
+	0xF7, 0xFB, 0x00, 0xAF, 0xAC, 0xAB, 0xF3, 0x00, /* 0xB8-0xBF */
+	0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, /* 0xC0-0xC7 */
+	0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, /* 0xC8-0xCF */
+	0x00, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, /* 0xD0-0xD7 */
+	0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, /* 0xD8-0xDF */
+	0xA1, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, /* 0xE0-0xE7 */
+	0x8A, 0x82, 0x88, 0x89, 0x8D, 0x00, 0x8C, 0x8B, /* 0xE8-0xEF */
+	0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, /* 0xF0-0xF7 */
+	0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98  /* 0xF8-0xFF */
+};
+
+
+unsigned char page25[256] = {
+	0xC4, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, /* 0x08-0x0F */
+	0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0xD9, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, /* 0x18-0x1F */
+	0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, /* 0x28-0x2F */
+	0x00, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, /* 0x38-0x3F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4F */
+	0xCD, 0xBA, 0x00, 0x00, 0xC9, 0x00, 0x00, 0xBB, /* 0x50-0x57 */
+	0x00, 0x00, 0xC8, 0x00, 0x00, 0xBC, 0x00, 0x00, /* 0x58-0x5F */
+	0xCC, 0x00, 0x00, 0xB9, 0x00, 0x00, 0xCB, 0x00, /* 0x60-0x67 */
+	0x00, 0xCA, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, /* 0x68-0x6F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7F */
+
+	0xDF, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+	0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9F */
+	0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAF */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBF */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCF */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDF */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEF */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* 0xF8-0xFF */
+};
+
+
+unsigned char *fat_uni2asc_pg[256] = {
+	page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL,   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL,   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL,   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL,   NULL, NULL, NULL, page25
+};
+
+/* Conversion from ASCII name characters to the shortname character
+ * should probably just just use XXX
+ */
+unsigned char fat_a2alias[256] = 
+{
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+	0x80, 0x9A, 0x90, 0xB6, 0x8E, 0xB7, 0x8F, 0x80,	/* 0x80-0x87 */
+	0xD2, 0xD3, 0xD4, 0xD8, 0xD7, 0xDE, 0x8E, 0x8F,	/* 0x88-0x8F */
+	0x90, 0x92, 0x92, 0xE2, 0x99, 0xE3, 0xEA, 0xEB, /* 0x90-0x97 */
+/*_~1*/	0x98, 0x99, 0x9A, 0x9D, 0x9C, 0x9D, 0x9E, 0x9F, /* 0x98-0x9F */
+	0xB5, 0xD6, 0xE0, 0xE9, 0xA5, 0xA5, 0xA6, 0xA7, /* 0xA0-0xA7 */
+	0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, /* 0xA8-0xAF */
+	0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, /* 0xB0-0xB7 */
+	0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, /* 0xB8-0xBF */
+	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, /* 0xC0-0xC7 */
+	0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, /* 0xC8-0xCF */
+	0xD1, 0xD1, 0xD2, 0xD3, 0xD4, 0x49, 0xD6, 0xD7, /* 0xD0-0xD7 */
+	0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, /* 0xD8-0xDF */
+	0xE0, 0xE1, 0xE2, 0xE3, 0x05, 0x05, 0xE6, 0xE8, /* 0xE0-0xE7 */
+	0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xED, 0xEE, 0xEF, /* 0xE8-0xEF */
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, /* 0xF0-0xF7 */
+	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF  /* 0xF8-0xFF */
+};
+
+struct unicode_value fat_a2uni[256] = {
+/* 0x00 */
+{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+{0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+{0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0x0B, 0x00},
+{0x0C, 0x00}, {0x0D, 0x00}, {0x0E, 0x00}, {0x0F, 0x00},
+/* 0x10 */
+{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+{0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+{0x18, 0x00}, {0x19, 0x00}, {0x1A, 0x00}, {0x1B, 0x00},
+{0x1C, 0x00}, {0x1D, 0x00}, {0x1E, 0x00}, {0x1F, 0x00},
+/* 0x20 */
+{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+{0x28, 0x00}, {0x29, 0x00}, {0x2A, 0x00}, {0x2B, 0x00},
+{0x2C, 0x00}, {0x2D, 0x00}, {0x2E, 0x00}, {0x2F, 0x00},
+/* 0x30 */
+{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+{0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+{0x38, 0x00}, {0x39, 0x00}, {0x3A, 0x00}, {0x3B, 0x00},
+{0x3C, 0x00}, {0x3D, 0x00}, {0x3E, 0x00}, {0x3F, 0x00},
+/* 0x40 */
+{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+{0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+{0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x00}, {0x4B, 0x00},
+{0x4C, 0x00}, {0x4D, 0x00}, {0x4E, 0x00}, {0x4F, 0x00},
+/* 0x50 */
+{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+{0x58, 0x00}, {0x59, 0x00}, {0x5A, 0x00}, {0x5B, 0x00},
+{0x5C, 0x00}, {0x5D, 0x00}, {0x5E, 0x00}, {0x5F, 0x00},
+/* 0x60 */
+{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+{0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+{0x68, 0x00}, {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00},
+{0x6C, 0x00}, {0x6D, 0x00}, {0x6E, 0x00}, {0x6F, 0x00},
+/* 0x70 */
+{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+{0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+{0x78, 0x00}, {0x79, 0x00}, {0x7A, 0x00}, {0x7B, 0x00},
+{0x7C, 0x00}, {0x7D, 0x00}, {0x7E, 0x00}, {0x7F, 0x00},
+/* 0x80 */
+{0xC7, 0x00}, {0xFC, 0x00}, {0xE9, 0x00}, {0xE2, 0x00},
+{0xE4, 0x00}, {0xE0, 0x00}, {0xE5, 0x00}, {0xE7, 0x00},
+{0xEA, 0x00}, {0xEB, 0x00}, {0xE8, 0x00}, {0xEF, 0x00},
+{0xEE, 0x00}, {0xEC, 0x00}, {0xC4, 0x00}, {0xC5, 0x00},
+/* 0x90 */
+{0xC9, 0x00}, {0xE6, 0x00}, {0xC6, 0x00}, {0xF4, 0x00},
+{0xF6, 0x00}, {0xF2, 0x00}, {0xFB, 0x00}, {0xF9, 0x00},
+{0xFF, 0x00}, {0xD6, 0x00}, {0xDC, 0x00}, {0xF8, 0x00},
+{0xA3, 0x00}, {0xD8, 0x00}, {0xD7, 0x00}, {0x92, 0x00},
+/* 0xA0 */
+{0xE1, 0x00}, {0xE0, 0x00}, {0xF3, 0x00}, {0xFA, 0x00},
+{0xF1, 0x00}, {0xD1, 0x00}, {0xAA, 0x00}, {0xBA, 0x00},
+{0xBF, 0x00}, {0xAE, 0x00}, {0xAC, 0x00}, {0xBD, 0x00},
+{0xBC, 0x00}, {0xA1, 0x00}, {0xAB, 0x00}, {0xBB, 0x00},
+/* 0xB0 */
+{0x91, 0x25}, {0x92, 0x25}, {0x93, 0x25}, {0x02, 0x25},
+{0x24, 0x25}, {0xC1, 0x00}, {0xC2, 0x00}, {0xC0, 0x00},
+{0xA9, 0x00}, {0x63, 0x25}, {0x51, 0x25}, {0x57, 0x25},
+{0x5D, 0x25}, {0xA2, 0x00}, {0xA5, 0x00}, {0x10, 0x25},
+/* 0xC0 */
+{0x14, 0x25}, {0x34, 0x25}, {0x2C, 0x25}, {0x1C, 0x25},
+{0x00, 0x25}, {0x3C, 0x25}, {0xE3, 0x00}, {0xC3, 0x00},
+{0x5A, 0x25}, {0x54, 0x25}, {0x69, 0x25}, {0x66, 0x25},
+{0x60, 0x25}, {0x50, 0x25}, {0x6C, 0x25}, {0xA4, 0x00},
+/* 0xD0 */
+{0xF0, 0x00}, {0xD0, 0x00}, {0xCA, 0x00}, {0xCB, 0x00},
+{0xC8, 0x00}, {0x31, 0x01}, {0xCD, 0x00}, {0xCE, 0x00},
+{0xCF, 0x00}, {0x18, 0x25}, {0x0C, 0x25}, {0x88, 0x25},
+{0x84, 0x25}, {0xA6, 0x00}, {0xCC, 0x00}, {0x80, 0x25},
+/* 0xE0 */
+{0xD3, 0x00}, {0xDF, 0x00}, {0xD4, 0x00}, {0xD2, 0x00},
+{0xF5, 0x00}, {0xD5, 0x00}, {0xB5, 0x00}, {0xFE, 0x00},
+{0xDE, 0x00}, {0xDA, 0x00}, {0xDB, 0x00}, {0xD9, 0x00},
+{0xFD, 0x00}, {0xDD, 0x00}, {0xAF, 0x00}, {0xB4, 0x00},
+/* 0xF0 */
+{0xAD, 0x00}, {0xB1, 0x00}, {0x17, 0x20}, {0xBE, 0x00},
+{0xB6, 0x00}, {0xA7, 0x00}, {0xF7, 0x00}, {0xB8, 0x00},
+{0xB0, 0x00}, {0xA8, 0x00}, {0xB7, 0x00}, {0xB9, 0x00},
+{0xB3, 0x00}, {0xB2, 0x00}, {0xA0, 0x25}, {0xA0, 0x00}};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.59/linux/fs/fat/tables.h linux/fs/fat/tables.h
--- v1.3.59/linux/fs/fat/tables.h	Thu Jan  1 02:00:00 1970
+++ linux/fs/fat/tables.h	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,26 @@
+struct unicode_value {
+	unsigned char uni1;
+	unsigned char uni2;
+};
+
+extern unsigned char fat_a2alias[];		/* Ascii to alias name conversion table */
+extern struct unicode_value fat_a2uni[];	/* Ascii to Unicode conversion table */
+
+extern unsigned char *fat_uni2asc_pg[];
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.59/linux/fs/filesystems.c linux/fs/filesystems.c
--- v1.3.59/linux/fs/filesystems.c	Tue Jan 23 21:15:50 1996
+++ linux/fs/filesystems.c	Wed Feb  7 09:39:28 1996
@@ -59,12 +59,20 @@
 	init_minix_fs();
 #endif
 
-#ifdef CONFIG_UMSDOS_FS
-	init_umsdos_fs();
+#ifdef CONFIG_FAT_FS
+	init_fat_fs();
 #endif
 
 #ifdef CONFIG_MSDOS_FS
 	init_msdos_fs();
+#endif
+
+#ifdef CONFIG_VFAT_FS
+	init_vfat_fs();
+#endif
+
+#ifdef CONFIG_UMSDOS_FS
+	init_umsdos_fs();
 #endif
 
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v1.3.59/linux/fs/inode.c linux/fs/inode.c
--- v1.3.59/linux/fs/inode.c	Sun Jan 14 16:30:12 1996
+++ linux/fs/inode.c	Wed Feb  7 13:44:42 1996
@@ -244,6 +244,13 @@
 /* POSIX UID/GID verification for setting inode attributes */
 int inode_change_ok(struct inode *inode, struct iattr *attr)
 {
+	/*
+	 *	If force is set do it anyway.
+	 */
+	 
+	if (attr->ia_valid & ATTR_FORCE)
+		return 0;
+
 	/* Make sure a caller can chown */
 	if ((attr->ia_valid & ATTR_UID) &&
 	    (current->fsuid != inode->i_uid ||
@@ -273,8 +280,6 @@
 	if ((attr->ia_valid & ATTR_MTIME_SET) &&
 	    ((current->fsuid != inode->i_uid) && !fsuser()))
 		return -EPERM;
-
-
 	return 0;
 }
 
@@ -315,13 +320,12 @@
 {
 	int retval;
 
-	if (attr->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME)) {
-		unsigned long now = CURRENT_TIME;
+	attr->ia_ctime = CURRENT_TIME;
+	if (attr->ia_valid & (ATTR_ATIME | ATTR_MTIME)) {
 		if (!(attr->ia_valid & ATTR_ATIME_SET))
-			attr->ia_atime = now;
+			attr->ia_atime = attr->ia_ctime;
 		if (!(attr->ia_valid & ATTR_MTIME_SET))
-			attr->ia_mtime = now;
-		attr->ia_ctime = now;
+			attr->ia_mtime = attr->ia_ctime;
 	}
 
 	if (inode->i_sb && inode->i_sb->s_op  &&
diff -u --recursive --new-file v1.3.59/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.3.59/linux/fs/isofs/inode.c	Tue Jan 23 21:15:50 1996
+++ linux/fs/isofs/inode.c	Wed Feb  7 07:47:19 1996
@@ -492,8 +492,8 @@
 	inode->i_size = isonum_733 (raw_inode->size);
 
 	/* There are defective discs out there - we do this to protect
-	   ourselves.  A cdrom will never contain more than 700Mb */
-	if((inode->i_size < 0 || inode->i_size > 700000000) &&
+	   ourselves.  A cdrom will never contain more than 800Mb */
+	if((inode->i_size < 0 || inode->i_size > 800000000) &&
 	    inode->i_sb->u.isofs_sb.s_cruft == 'n') {
 	  printk("Warning: defective cdrom.  Enabling \"cruft\" mount option.\n");
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/Makefile linux/fs/msdos/Makefile
--- v1.3.59/linux/fs/msdos/Makefile	Tue Jan 23 21:15:50 1996
+++ linux/fs/msdos/Makefile	Wed Feb  7 09:39:28 1996
@@ -1,5 +1,5 @@
 #
-# Makefile for the linux MS-DOS-filesystem routines.
+# Makefile for the linux msdos-filesystem routines.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -8,8 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := msdos.o
-O_OBJS   := buffer.o namei.o file.o dir.o misc.o fat.o mmap.o
-OX_OBJS  := inode.o
+O_OBJS   := namei.o
+OX_OBJS  := msdosfs_syms.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/buffer.c linux/fs/msdos/buffer.c
--- v1.3.59/linux/fs/msdos/buffer.c	Mon Nov 27 12:48:32 1995
+++ linux/fs/msdos/buffer.c	Thu Jan  1 02:00:00 1970
@@ -1,150 +0,0 @@
-/*
- * linux/fs/msdos/buffer.c
- *
- *
- */
-
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-
-struct buffer_head *msdos_bread (
-	struct super_block *sb,
-	int block)
-{
-	struct buffer_head *ret = NULL;
-	if (sb->s_blocksize == 512){
-		ret = bread (sb->s_dev,block,512);
-	}else{
-		struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
-		if (real != NULL){
-			ret = (struct buffer_head *)kmalloc (sizeof(struct buffer_head)
-				,GFP_KERNEL);
-			if (ret != NULL){
-				/* #Specification: msdos / strategy / special device / dummy blocks
-					Many special device (Scsi optical disk for one) use
-					larger hardware sector size. This allows for higher
-					capacity.
-
-					Most of the time, the MsDOS file system that sit
-					on this device is totally unaligned. It use logically
-					512 bytes sector size, with logical sector starting
-					in the middle of a hardware block. The bad news is
-					that a hardware sector may hold data own by two
-					different files. This means that the hardware sector
-					must be read, patch and written almost all the time.
-
-					Needless to say that it kills write performance
-					on all OS.
-
-					Internally the linux msdos fs is using 512 bytes
-					logical sector. When accessing such a device, we
-					allocate dummy buffer cache blocks, that we stuff
-					with the information of a real one (1k large).
-
-					This strategy is used to hide this difference to
-					the core of the msdos fs. The slowdown is not
-					hidden though!
-				*/
-				/*
-					The memset is there only to catch errors. The msdos
-					fs is only using b_data
-				*/
-				memset (ret,0,sizeof(*ret));
-				ret->b_data = real->b_data;
-				if (block & 1) ret->b_data += 512;
-				ret->b_next = real;
-			}else{
-				brelse (real);
-			}
-		}
-	}
-	return ret;
-}
-struct buffer_head *msdos_getblk (
-	struct super_block *sb,
-	int block)
-{
-	struct buffer_head *ret = NULL;
-	if (sb->s_blocksize == 512){
-		ret = getblk (sb->s_dev,block,512);
-	}else{
-		/* #Specification: msdos / special device / writing
-			A write is always preceded by a read of the complete block
-			(large hardware sector size). This defeat write performance.
-			There is a possibility to optimize this when writing large
-			chunk by making sure we are filling large block. Volunteer ?
-		*/
-		ret = msdos_bread (sb,block);
-	}
-	return ret;
-}
-
-void msdos_brelse (
-	struct super_block *sb,
-	struct buffer_head *bh)
-{
-	if (bh != NULL){
-		if (sb->s_blocksize == 512){
-			brelse (bh);
-		}else{
-			brelse (bh->b_next);
-			/* We can free the dummy because a new one is allocated at
-				each msdos_getblk() and msdos_bread().
-			*/
-			kfree (bh);
-		}
-	}
-}
-	
-void msdos_mark_buffer_dirty (
-	struct super_block *sb,
-	struct buffer_head *bh,
-	int dirty_val)
-{
-	if (sb->s_blocksize != 512){
-		bh = bh->b_next;
-	}
-	mark_buffer_dirty (bh,dirty_val);
-}
-
-void msdos_set_uptodate (
-	struct super_block *sb,
-	struct buffer_head *bh,
-	int val)
-{
-	if (sb->s_blocksize != 512){
-		bh = bh->b_next;
-	}
-	mark_buffer_uptodate(bh, val);
-}
-int msdos_is_uptodate (
-	struct super_block *sb,
-	struct buffer_head *bh)
-{
-	if (sb->s_blocksize != 512){
-		bh = bh->b_next;
-	}
-	return buffer_uptodate(bh);
-}
-
-void msdos_ll_rw_block (
-	struct super_block *sb,
-	int opr,
-	int nbreq,
-	struct buffer_head *bh[32])
-{
-	if (sb->s_blocksize == 512){
-		ll_rw_block(opr,nbreq,bh);
-	}else{
-		struct buffer_head *tmp[32];
-		int i;
-		for (i=0; i<nbreq; i++){
-			tmp[i] = bh[i]->b_next;
-		}
-		ll_rw_block(opr,nbreq,tmp);
-	}
-}
-
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/dir.c linux/fs/msdos/dir.c
--- v1.3.59/linux/fs/msdos/dir.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/msdos/dir.c	Thu Jan  1 02:00:00 1970
@@ -1,135 +0,0 @@
-/*
- *  linux/fs/msdos/dir.c
- *  MS-DOS directory handling functions
- *
- *  Written 1992,1993 by Werner Almesberger
- *
- *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
- */
-
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-
-#include <asm/segment.h>
-
-#include "msbuffer.h"
-
-
-#define PRINTK(X)
-
-static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
-{
-	return -EISDIR;
-}
-
-static struct file_operations msdos_dir_operations = {
-	NULL,			/* lseek - default */
-	msdos_dir_read,		/* read */
-	NULL,			/* write - bad */
-	msdos_readdir,		/* readdir */
-	NULL,			/* select - default */
-	NULL,			/* ioctl - default */
-	NULL,			/* mmap */
-	NULL,			/* no special open code */
-	NULL,			/* no special release code */
-	file_fsync		/* fsync */
-};
-
-struct inode_operations msdos_dir_inode_operations = {
-	&msdos_dir_operations,	/* default directory file-ops */
-	msdos_create,		/* create */
-	msdos_lookup,		/* lookup */
-	NULL,			/* link */
-	msdos_unlink,		/* unlink */
-	NULL,			/* symlink */
-	msdos_mkdir,		/* mkdir */
-	msdos_rmdir,		/* rmdir */
-	NULL,			/* mknod */
-	msdos_rename,		/* rename */
-	NULL,			/* readlink */
-	NULL,			/* follow_link */
-	NULL,			/* readpage */
-	NULL,			/* writepage */
-	msdos_bmap,		/* bmap */
-	NULL,			/* truncate */
-	NULL			/* permission */
-};
-
-int msdos_readdir(
-	struct inode *inode,
-	struct file *filp,
-	void *dirent,
-	filldir_t filldir)
-{
-	struct super_block *sb = inode->i_sb;
-	int ino,i,i2,last;
-	char c;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	unsigned long oldpos = filp->f_pos;
-
-	if (!inode || !S_ISDIR(inode->i_mode))
-		return -EBADF;
-/* Fake . and .. for the root directory. */
-	if (inode->i_ino == MSDOS_ROOT_INO) {
-		while (oldpos < 2) {
-			if (filldir(dirent, "..", oldpos+1, oldpos, MSDOS_ROOT_INO) < 0)
-				return 0;
-			oldpos++;
-			filp->f_pos++;
-		}
-		if (oldpos == 2)
-			filp->f_pos = 0;
-	}
-	if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
-		return -ENOENT;
-	bh = NULL;
-	while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
-		if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
-			char bufname[13];
-			char *ptname = bufname;
-			int dotoffset = 0;
-			if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->dotsOK) {
-				bufname[0] = '.';
-				dotoffset = 1;
-				ptname = bufname+1;
-			}
-			for (i = last = 0; i < 8; i++) {
-				if (!(c = de->name[i])) break;
-				if (c >= 'A' && c <= 'Z') c += 32;
-				/* see namei.c, msdos_format_name */
-				if (c == 0x05) c = 0xE5;
-				if (c != ' ')
-					last = i+1;
-				ptname[i] = c;
-			}
-			i = last;
-			ptname[i] = '.';
-			i++;
-			for (i2 = 0; i2 < 3; i2++) {
-				if (!(c = de->ext[i2])) break;
-				if (c >= 'A' && c <= 'Z') c += 32;
-				if (c != ' ')
-					last = i+1;
-				ptname[i] = c;
-				i++;
-			}
-			if ((i = last) != 0) {
-				if (!strcmp(de->name,MSDOS_DOT))
-					ino = inode->i_ino;
-				else if (!strcmp(de->name,MSDOS_DOTDOT))
-					ino = msdos_parent_ino(inode,0);
-				if (filldir(dirent, bufname, i+dotoffset, oldpos, ino) < 0) {
-					filp->f_pos = oldpos;
-					break;
-				}
-			}
-		}
-		oldpos = filp->f_pos;
-	}
-	if (bh) brelse(bh);
-	return 0;
-}
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/fat.c linux/fs/msdos/fat.c
--- v1.3.59/linux/fs/msdos/fat.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/msdos/fat.c	Thu Jan  1 02:00:00 1970
@@ -1,297 +0,0 @@
-/*
- *  linux/fs/msdos/fat.c
- *
- *  Written 1992,1993 by Werner Almesberger
- */
-
-#include <linux/msdos_fs.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-
-#include "msbuffer.h"
-
-static struct fat_cache *fat_cache,cache[FAT_CACHE];
-
-/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
-   new_value is != -1, that FAT entry is replaced by it. */
-
-int fat_access(struct super_block *sb,int nr,int new_value)
-{
-	struct buffer_head *bh,*bh2,*c_bh,*c_bh2;
-	unsigned char *p_first,*p_last;
-	int first,last,next,copy;
-
-	if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters) return 0;
-	if (MSDOS_SB(sb)->fat_bits == 16) first = last = nr*2;
-	else {
-		first = nr*3/2;
-		last = first+1;
-	}
-	if (!(bh = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >>
-	    SECTOR_BITS),SECTOR_SIZE))) {
-		printk("bread in fat_access failed\n");
-		return 0;
-	}
-	if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS))
-		bh2 = bh;
-	else {
-		if (!(bh2 = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last
-		    >> SECTOR_BITS),SECTOR_SIZE))) {
-			brelse(bh);
-			printk("bread in fat_access failed\n");
-			return 0;
-		}
-	}
-	if (MSDOS_SB(sb)->fat_bits == 16) {
-		p_first = p_last = NULL; /* GCC needs that stuff */
-		next = CF_LE_W(((unsigned short *) bh->b_data)[(first &
-		    (SECTOR_SIZE-1)) >> 1]);
-		if (next >= 0xfff7) next = -1;
-	}
-	else {
-		p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
-		p_last = &((unsigned char *) bh2->b_data)[(first+1) &
-		    (SECTOR_SIZE-1)];
-		if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
-		else next = (*p_first+(*p_last << 8)) & 0xfff;
-		if (next >= 0xff7) next = -1;
-	}
-	if (new_value != -1) {
-		if (MSDOS_SB(sb)->fat_bits == 16)
-			((unsigned short *) bh->b_data)[(first & (SECTOR_SIZE-1)) >>
-			    1] = CT_LE_W(new_value);
-		else {
-			if (nr & 1) {
-				*p_first = (*p_first & 0xf) | (new_value << 4);
-				*p_last = new_value >> 4;
-			}
-			else {
-				*p_first = new_value & 0xff;
-				*p_last = (*p_last & 0xf0) | (new_value >> 8);
-			}
-			mark_buffer_dirty(bh2, 1);
-		}
-		mark_buffer_dirty(bh, 1);
-		for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
-			if (!(c_bh = bread(sb->s_dev,MSDOS_SB(sb)->
-			    fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)->
-			    fat_length*copy,SECTOR_SIZE))) break;
-			memcpy(c_bh->b_data,bh->b_data,SECTOR_SIZE);
-			mark_buffer_dirty(c_bh, 1);
-			if (bh != bh2) {
-				if (!(c_bh2 = bread(sb->s_dev,
-				    MSDOS_SB(sb)->fat_start+(first >>
-				    SECTOR_BITS)+MSDOS_SB(sb)->fat_length*copy
-				    +1,SECTOR_SIZE))) {
-					brelse(c_bh);
-					break;
-				}
-				memcpy(c_bh2->b_data,bh2->b_data,SECTOR_SIZE);
-				brelse(c_bh2);
-			}
-			brelse(c_bh);
-		}
-	}
-	brelse(bh);
-	if (bh != bh2) brelse(bh2);
-	return next;
-}
-
-
-void cache_init(void)
-{
-	static int initialized = 0;
-	int count;
-
-	if (initialized) return;
-	fat_cache = &cache[0];
-	for (count = 0; count < FAT_CACHE; count++) {
-		cache[count].device = 0;
-		cache[count].next = count == FAT_CACHE-1 ? NULL :
-		    &cache[count+1];
-	}
-	initialized = 1;
-}
-
-
-void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
-{
-	struct fat_cache *walk;
-
-#ifdef DEBUG
-printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
-       inode->i_ino, cluster, *f_clu, *d_clu);
-#endif
-	for (walk = fat_cache; walk; walk = walk->next)
-		if (inode->i_dev == walk->device
-		    && walk->ino == inode->i_ino
-		    && walk->file_cluster <= cluster
-		    && walk->file_cluster > *f_clu) {
-			*d_clu = walk->disk_cluster;
-#ifdef DEBUG
-printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
-#endif
-			if ((*f_clu = walk->file_cluster) == cluster) return;
-		}
-#ifdef DEBUG
-printk("cache miss\n");
-#endif
-}
-
-
-#ifdef DEBUG
-static void list_cache(void)
-{
-	struct fat_cache *walk;
-
-	for (walk = fat_cache; walk; walk = walk->next) {
-		if (walk->device)
-			printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
-			       walk->ino, walk->file_cluster, walk->disk_cluster);
-		else printk("-- ");
-	}
-	printk("\n");
-}
-#endif
-
-
-void cache_add(struct inode *inode,int f_clu,int d_clu)
-{
-	struct fat_cache *walk,*last;
-
-#ifdef DEBUG
-printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
-       inode->i_ino, f_clu, d_clu);
-#endif
-	last = NULL;
-	for (walk = fat_cache; walk->next; walk = (last = walk)->next)
-		if (inode->i_dev == walk->device
-		    && walk->ino == inode->i_ino
-		    && walk->file_cluster == f_clu) {
-			if (walk->disk_cluster != d_clu) {
-				printk("FAT cache corruption");
-				cache_inval_inode(inode);
-				return;
-			}
-			/* update LRU */
-			if (last == NULL) return;
-			last->next = walk->next;
-			walk->next = fat_cache;
-			fat_cache = walk;
-#ifdef DEBUG
-list_cache();
-#endif
-			return;
-		}
-	walk->device = inode->i_dev;
-	walk->ino = inode->i_ino;
-	walk->file_cluster = f_clu;
-	walk->disk_cluster = d_clu;
-	last->next = NULL;
-	walk->next = fat_cache;
-	fat_cache = walk;
-#ifdef DEBUG
-list_cache();
-#endif
-}
-
-
-/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
-   fixes itself after a while. */
-
-void cache_inval_inode(struct inode *inode)
-{
-	struct fat_cache *walk;
-
-	for (walk = fat_cache; walk; walk = walk->next)
-		if (walk->device == inode->i_dev
-		    && walk->ino == inode->i_ino)
-			walk->device = 0;
-}
-
-
-void cache_inval_dev(kdev_t device)
-{
-	struct fat_cache *walk;
-
-	for (walk = fat_cache; walk; walk = walk->next)
-		if (walk->device == device)
-			walk->device = 0;
-}
-
-
-int get_cluster(struct inode *inode,int cluster)
-{
-	int nr,count;
-
-	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
-	if (!cluster) return nr;
-	count = 0;
-	for (cache_lookup(inode,cluster,&count,&nr); count < cluster;
-	    count++) {
-		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
-		if (!nr) return 0;
-	}
-	cache_add(inode,cluster,nr);
-	return nr;
-}
-
-
-int msdos_smap(struct inode *inode,int sector)
-{
-	struct msdos_sb_info *sb;
-	int cluster,offset;
-
-	sb = MSDOS_SB(inode->i_sb);
-	if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
-	    !MSDOS_I(inode)->i_start)) {
-		if (sector >= sb->dir_entries >> MSDOS_DPS_BITS) return 0;
-		return sector+sb->dir_start;
-	}
-	cluster = sector/sb->cluster_size;
-	offset = sector % sb->cluster_size;
-	if (!(cluster = get_cluster(inode,cluster))) return 0;
-	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
-}
-
-
-/* Free all clusters after the skip'th cluster. Doesn't use the cache,
-   because this way we get an additional sanity check. */
-
-int fat_free(struct inode *inode,int skip)
-{
-	int nr,last;
-
-	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
-	last = 0;
-	while (skip--) {
-		last = nr;
-		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
-		if (!nr) {
-			printk("fat_free: skipped EOF\n");
-			return -EIO;
-		}
-	}
-	if (last)
-		fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits ==
-		    12 ? 0xff8 : 0xfff8);
-	else {
-		MSDOS_I(inode)->i_start = 0;
-		inode->i_dirt = 1;
-	}
-	lock_fat(inode->i_sb);
-	while (nr != -1) {
-		if (!(nr = fat_access(inode->i_sb,nr,0))) {
-			fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
-			break;
-		}
-		if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
-			MSDOS_SB(inode->i_sb)->free_clusters++;
-		inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
-	}
-	unlock_fat(inode->i_sb);
-	cache_inval_inode(inode);
-	return 0;
-}
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/file.c linux/fs/msdos/file.c
--- v1.3.59/linux/fs/msdos/file.c	Thu Jan  4 21:54:58 1996
+++ linux/fs/msdos/file.c	Thu Jan  1 02:00:00 1970
@@ -1,375 +0,0 @@
-/*
- *  linux/fs/msdos/file.c
- *
- *  Written 1992,1993 by Werner Almesberger
- *
- *  MS-DOS regular file handling primitives
- */
-
-#include <linux/sched.h>
-#include <linux/locks.h>
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
-#include "msbuffer.h"
-
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-#define MAX(a,b) (((a) > (b)) ? (a) : (b))
-
-#define PRINTK(x)
-#define Printk(x) printk x
-
-static struct file_operations msdos_file_operations = {
-	NULL,			/* lseek - default */
-	msdos_file_read,	/* read */
-	msdos_file_write,	/* write */
-	NULL,			/* readdir - bad */
-	NULL,			/* select - default */
-	NULL,			/* ioctl - default */
-	generic_mmap,		/* mmap */
-	NULL,			/* no special open is needed */
-	NULL,			/* release */
-	file_fsync		/* fsync */
-};
-
-struct inode_operations msdos_file_inode_operations = {
-	&msdos_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 */
-	msdos_bmap,		/* bmap */
-	msdos_truncate,		/* truncate */
-	NULL,			/* permission */
-	NULL			/* smap */
-};
-/* #Specification: msdos / special devices / mmap	
-	Mmapping does work because a special mmap is provide in that case.
-	Note that it is much less efficient than the generic_mmap normally
-	used since it allocate extra buffer. generic_mmap is used for
-	normal device (512 bytes hardware sectors).
-*/
-static struct file_operations msdos_file_operations_1024 = {
-	NULL,			/* lseek - default */
-	msdos_file_read,	/* read */
-	msdos_file_write,	/* write */
-	NULL,			/* readdir - bad */
-	NULL,			/* select - default */
-	NULL,			/* ioctl - default */
-	msdos_mmap,		/* mmap */
-	NULL,			/* no special open is needed */
-	NULL,			/* release */
-	file_fsync		/* fsync */
-};
-
-/* #Specification: msdos / special devices / swap file
-	Swap file can't work on special devices with a large sector
-	size (1024 bytes hard sector). Those devices have a weird
-	MsDOS filesystem layout. Generally a single hardware sector
-	may contain 2 unrelated logical sector. This mean that there is
-	no easy way to do a mapping between disk sector of a file and virtual
-	memory. So swap file is difficult (not available right now)
-	on those devices. Off course, Ext2 does not have this problem.
-*/
-struct inode_operations msdos_file_inode_operations_1024 = {
-	&msdos_file_operations_1024,	/* 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,			/* readpage */
-	NULL,			/* writepage */
-	NULL,			/* bmap */
-	msdos_truncate,		/* truncate */
-	NULL,			/* permission */
-	NULL			/* smap */
-};
-
-#define MSDOS_PREFETCH	32
-struct msdos_pre {
-	int file_sector;/* Next sector to read in the prefetch table */
-			/* This is relative to the file, not the disk */
-	struct buffer_head *bhlist[MSDOS_PREFETCH];	/* All buffers needed */
-	int nblist;	/* Number of buffers in bhlist */
-	int nolist;	/* index in bhlist */
-};
-/*
-	Order the prefetch of more sectors.
-*/
-static void msdos_prefetch (
-	struct inode *inode,
-	struct msdos_pre *pre,
-	int nb)		/* How many must be prefetch at once */
-{
-	struct super_block *sb = inode->i_sb;
-	struct buffer_head *bhreq[MSDOS_PREFETCH];	/* Buffers not */
-												/* already read */
-	int nbreq=0;			/* Number of buffers in bhreq */
-	int i;
-	for (i=0; i<nb; i++){
-		int sector = msdos_smap(inode,pre->file_sector);
-		if (sector != 0){
-			struct buffer_head *bh;
-			PRINTK (("fsector2 %d -> %d\n",pre->file_sector-1,sector));
-			pre->file_sector++;
-			bh = getblk(inode->i_dev,sector,SECTOR_SIZE);
-			if (bh == NULL)	break;
-			pre->bhlist[pre->nblist++] = bh;
-			if (!msdos_is_uptodate(sb,bh)) bhreq[nbreq++] = bh;
-		}else{
-			break;
-		}
-	}
-	if (nbreq > 0) msdos_ll_rw_block (sb,READ,nbreq,bhreq);
-	for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL;
-}
-
-/*
-	Read a file into user space
-*/
-int msdos_file_read(
-	struct inode *inode,
-	struct file *filp,
-	char *buf,
-	int count)
-{
-	struct super_block *sb = inode->i_sb;
-	char *start = buf;
-	char *end   = buf + count;
-	int i;
-	int left_in_file;
-	struct msdos_pre pre;
-		
-
-	if (!inode) {
-		printk("msdos_file_read: inode = NULL\n");
-		return -EINVAL;
-	}
-	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
-	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
-		printk("msdos_file_read: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	if (filp->f_pos >= inode->i_size || count <= 0) return 0;
-	/*
-		Tell the buffer cache which block we expect to read in advance
-		Since we are limited with the stack, we preread only MSDOS_PREFETCH
-		because we have to keep the result into the local
-		arrays pre.bhlist and bhreq.
-		
-		Each time we process one block in bhlist, we replace
-		it by a new prefetch block if needed.
-	*/
-	PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,filp->f_pos,inode->i_size,count));
-	{
-		/*
-			We must prefetch complete block, so we must
-			take in account the offset in the first block.
-		*/
-		int count_max = (filp->f_pos & (SECTOR_SIZE-1)) + count;
-		int   to_reada;	/* How many block to read all at once */
-		pre.file_sector = filp->f_pos >> SECTOR_BITS;
-		to_reada = count_max / SECTOR_SIZE;
-		if (count_max & (SECTOR_SIZE-1)) to_reada++;
-		if (filp->f_reada || !MSDOS_I(inode)->i_binary){
-			/* Doing a read ahead on ascii file make sure we always */
-			/* pre read enough, since we don't know how many blocks */
-			/* we really need */
-			int ahead = read_ahead[MAJOR(inode->i_dev)];
-			PRINTK (("to_reada %d ahead %d\n",to_reada,ahead));
-			if (ahead == 0) ahead = 8;
-			to_reada += ahead;
-		}
-		if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH;
-		pre.nblist = 0;
-		msdos_prefetch (inode,&pre,to_reada);
-	}
-	pre.nolist = 0;
-	PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist));
-	while ((left_in_file = inode->i_size - filp->f_pos) > 0
-		&& buf < end){
-		struct buffer_head *bh = pre.bhlist[pre.nolist];
-		char *data;
-		int size,offset;
-		if (bh == NULL) break;
-		pre.bhlist[pre.nolist] = NULL;
-		pre.nolist++;
-		if (pre.nolist == MSDOS_PREFETCH/2){
-			memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2
-				,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0]));
-			pre.nblist -= MSDOS_PREFETCH/2;
-			msdos_prefetch (inode,&pre,MSDOS_PREFETCH/2);
-			pre.nolist = 0;
-		}
-		PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count));
-		wait_on_buffer(bh);
-		if (!msdos_is_uptodate(sb,bh)){
-			/* read error  ? */
-			brelse (bh);
-			break;
-		}
-		offset = filp->f_pos & (SECTOR_SIZE-1);
-		data = bh->b_data + offset;
-		size = MIN(SECTOR_SIZE-offset,left_in_file);
-		if (MSDOS_I(inode)->i_binary) {
-			size = MIN(size,end-buf);
-			memcpy_tofs(buf,data,size);
-			buf += size;
-			filp->f_pos += size;
-		}else{
-			for (; size && buf < end; size--) {
-				char ch = *data++;
-				filp->f_pos++;
-				if (ch == 26){
-					filp->f_pos = inode->i_size;
-					break;
-				}else if (ch != '\r'){
-					put_user(ch,buf++);
-				}
-			}
-		}
-		brelse(bh);
-	}
-	PRINTK (("--- %d -> %d\n",count,(int)(buf-start)));
-	for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]);
-	if (start == buf) return -EIO;
-	if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME;
-	filp->f_reada = 1;	/* Will be reset if a lseek is done */
-	return buf-start;
-}
-
-/*
-	Write to a file either from user space
-*/
-int msdos_file_write(
-	struct inode *inode,
-	struct file *filp,
-	const char *buf,
-	int count)
-{
-	struct super_block *sb = inode->i_sb;
-	int sector,offset,size,left,written;
-	int error,carry;
-	const char *start;
-	char *to,ch;
-	struct buffer_head *bh;
-	int binary_mode = MSDOS_I(inode)->i_binary;
-
-	if (!inode) {
-		printk("msdos_file_write: inode = NULL\n");
-		return -EINVAL;
-	}
-	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
-	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
-		printk("msdos_file_write: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	/* system files are immutable */
-	if (IS_IMMUTABLE(inode)) return -EPERM;
-/*
- * ok, append may not work when many processes are writing at the same time
- * but so what. That way leads to madness anyway.
- */
-	if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
-	if (count <= 0) return 0;
-	error = carry = 0;
-	for (start = buf; count || carry; count -= size) {
-		while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
-			if ((error = msdos_add_cluster(inode)) < 0) break;
-		if (error) {
-			msdos_truncate(inode);
-			break;
-		}
-		offset = filp->f_pos & (SECTOR_SIZE-1);
-		size = MIN(SECTOR_SIZE-offset,MAX(carry,count));
-		if (binary_mode
-			&& offset == 0
-			&& (size == SECTOR_SIZE
-				|| filp->f_pos + size >= inode->i_size)){
-			/* No need to read the block first since we will */
-			/* completely overwrite it */
-			/* or at least write past the end of file */
-			if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){
-				error = -EIO;
-				break;
-			}
-		}else if (!(bh = bread(inode->i_dev,sector,SECTOR_SIZE))) {
-			error = -EIO;
-			break;
-		}
-		if (binary_mode) {
-			memcpy_fromfs(bh->b_data+offset,buf,written = size);
-			buf += size;
-		}
-		else {
-			written = left = SECTOR_SIZE-offset;
-			to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
-			if (carry) {
-				*to++ = '\n';
-				left--;
-				carry = 0;
-			}
-			for (size = 0; size < count && left; size++) {
-				if ((ch = get_user(buf++)) == '\n') {
-					*to++ = '\r';
-					left--;
-				}
-				if (!left) carry = 1;
-				else {
-					*to++ = ch;
-					left--;
-				}
-			}
-			written -= left;
-		}
-		filp->f_pos += written;
-		if (filp->f_pos > inode->i_size) {
-			inode->i_size = filp->f_pos;
-			inode->i_dirt = 1;
-		}
-		msdos_set_uptodate(sb,bh,1);
-		mark_buffer_dirty(bh, 0);
-		brelse(bh);
-	}
-	if (start == buf)
-		return error;
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
-	inode->i_dirt = 1;
-	return buf-start;
-}
-
-void msdos_truncate(struct inode *inode)
-{
-	int cluster;
-
-	/* Why no return value?  Surely the disk could fail... */
-	if (IS_IMMUTABLE(inode)) return /* -EPERM */;
-	cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
-	(void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
-	MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
-	inode->i_dirt = 1;
-}
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/inode.c linux/fs/msdos/inode.c
--- v1.3.59/linux/fs/msdos/inode.c	Tue Jan 23 21:15:50 1996
+++ linux/fs/msdos/inode.c	Thu Jan  1 02:00:00 1970
@@ -1,575 +0,0 @@
-/*
- *  linux/fs/msdos/inode.c
- *
- *  Written 1992,1993 by Werner Almesberger
- */
-
-#include <linux/module.h>
-
-#include <linux/msdos_fs.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/major.h>
-#include <linux/blkdev.h>
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/locks.h>
-
-#include "msbuffer.h"
-
-#include <asm/segment.h>
-
-void msdos_put_inode(struct inode *inode)
-{
-	struct inode *depend;
-	struct super_block *sb;
-
-	if (inode->i_nlink) {
-		if (MSDOS_I(inode)->i_busy) cache_inval_inode(inode);
-		return;
-	}
-	inode->i_size = 0;
-	msdos_truncate(inode);
-	depend = MSDOS_I(inode)->i_depend;
-	sb = inode->i_sb;
-	clear_inode(inode);
-	if (depend) {
-		if (MSDOS_I(depend)->i_old != inode) {
-			printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
-			    depend, inode, MSDOS_I(depend)->i_old);
-			fs_panic(sb,"...");
-			return;
-		}
-		MSDOS_I(depend)->i_old = NULL;
-		iput(depend);
-	}
-}
-
-
-void msdos_put_super(struct super_block *sb)
-{
-	cache_inval_dev(sb->s_dev);
-	set_blocksize (sb->s_dev,BLOCK_SIZE);
-	lock_super(sb);
-	sb->s_dev = 0;
-	unlock_super(sb);
-	MOD_DEC_USE_COUNT;
-	return;
-}
-
-
-static struct super_operations msdos_sops = { 
-	msdos_read_inode,
-	msdos_notify_change,
-	msdos_write_inode,
-	msdos_put_inode,
-	msdos_put_super,
-	NULL, /* added in 0.96c */
-	msdos_statfs,
-	NULL
-};
-
-
-static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
-    gid_t *gid,int *umask,int *debug,int *fat,int *quiet,
-	int *blksize, char *dotsOK)
-{
-	char *this_char,*value;
-
-	*check = 'n';
-	*conversion = 'b';
-	*dotsOK = 0;
-	*uid = current->uid;
-	*gid = current->gid;
-	*umask = current->fs->umask;
-	*debug = *fat = *quiet = 0;
-	if (!options) return 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
-		if (!strcmp(this_char,"check") && value) {
-			if (value[0] && !value[1] && strchr("rns",*value))
-				*check = *value;
-			else if (!strcmp(value,"relaxed")) *check = 'r';
-			else if (!strcmp(value,"normal")) *check = 'n';
-			else if (!strcmp(value,"strict")) *check = 's';
-			else return 0;
-		}
-		else if (!strcmp(this_char,"conv") && value) {
-			if (value[0] && !value[1] && strchr("bta",*value))
-				*conversion = *value;
-			else if (!strcmp(value,"binary")) *conversion = 'b';
-			else if (!strcmp(value,"text")) *conversion = 't';
-			else if (!strcmp(value,"auto")) *conversion = 'a';
-			else return 0;
-		}
-		else if (!strcmp(this_char,"dots")) {
-		  *dotsOK = 1;
-		}
-		else if (!strcmp(this_char,"nodots")) {
-		  *dotsOK = 0;
-		}
-		else if (!strcmp(this_char,"dotsOK") && value) {
-			if (!strcmp(value,"yes")) *dotsOK = 1;
-			else if (!strcmp(value,"no")) *dotsOK = 0;
-			else return 0;
-		}
-		else if (!strcmp(this_char,"uid")) {
-			if (!value || !*value)
-				return 0;
-			*uid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"gid")) {
-			if (!value || !*value)
-				return 0;
-			*gid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"umask")) {
-			if (!value || !*value)
-				return 0;
-			*umask = simple_strtoul(value,&value,8);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"debug")) {
-			if (value) return 0;
-			*debug = 1;
-		}
-		else if (!strcmp(this_char,"fat")) {
-			if (!value || !*value)
-				return 0;
-			*fat = simple_strtoul(value,&value,0);
-			if (*value || (*fat != 12 && *fat != 16))
-				return 0;
-		}
-		else if (!strcmp(this_char,"quiet")) {
-			if (value) return 0;
-			*quiet = 1;
-		}
-		else if (!strcmp(this_char,"blocksize")) {
-			*blksize = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-			if (*blksize != 512 && *blksize != 1024){
-				printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
-			}
-		}
-		else return 1;
-	}
-	return 1;
-}
-
-
-/* Read the super block of an MS-DOS FS. */
-
-struct super_block *msdos_read_super(struct super_block *sb,void *data,
-				     int silent)
-{
-	struct buffer_head *bh;
-	struct msdos_boot_sector *b;
-	int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
-	int debug,error,fat,quiet;
-	char check,conversion,dotsOK;
-	uid_t uid;
-	gid_t gid;
-	int umask;
-	int blksize = 512;
-
-	MOD_INC_USE_COUNT;
-	if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
-		blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)];
-		if (blksize != 512){
-			printk ("MSDOS: Hardware sector size is %d\n",blksize);
-		}
-	}
-	if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask,
-	    &debug,&fat,&quiet,&blksize,&dotsOK)
-		|| (blksize != 512 && blksize != 1024)) {
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	cache_init();
-	lock_super(sb);
-	/* The first read is always 1024 bytes */
-	sb->s_blocksize = 1024;
-	set_blocksize(sb->s_dev, 1024);
-	bh = bread(sb->s_dev, 0, 1024);
-	unlock_super(sb);
-	if (bh == NULL || !msdos_is_uptodate(sb,bh)) {
-		brelse (bh);
-		sb->s_dev = 0;
-		printk("MSDOS bread failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	b = (struct msdos_boot_sector *) bh->b_data;
-	set_blocksize(sb->s_dev, blksize);
-/*
- * The DOS3 partition size limit is *not* 32M as many people think.  
- * Instead, it is 64K sectors (with the usual sector size being
- * 512 bytes, leading to a 32M limit).
- * 
- * DOS 3 partition managers got around this problem by faking a 
- * larger sector size, ie treating multiple physical sectors as 
- * a single logical sector.
- * 
- * We can accommodate this scheme by adjusting our cluster size,
- * fat_start, and data_start by an appropriate value.
- *
- * (by Drew Eckhardt)
- */
-
-#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
-    /* don't divide by zero */
-
-	logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size);
-	sector_mult = logical_sector_size >> SECTOR_BITS;
-	MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
-	MSDOS_SB(sb)->fats = b->fats;
-	MSDOS_SB(sb)->fat_start = CF_LE_W(b->reserved)*sector_mult;
-	MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
-	MSDOS_SB(sb)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W(
-	    b->fat_length))*sector_mult;
-	MSDOS_SB(sb)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries
-	    ));
-	MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
-	    MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
-	    sector_mult);
-	data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ?
-	    CF_LE_W(*((unsigned short *) &b->sectors)) :
-	    CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
-	error = !b->cluster_size || !sector_mult;
-	if (!error) {
-		MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
-		    b->cluster_size/sector_mult : 0;
-		MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
-		    MSDOS_FAT12 ? 16 : 12;
-		fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
-		    MSDOS_SB(sb)->fat_bits;
-		error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
-		    (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
-		    MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
-		    || !b->secs_track || !b->heads;
-	}
-	brelse(bh);
-	/*
-		This must be done after the brelse because the bh is a dummy
-		allocated by msdos_bread (see buffer.c)
-	*/
-	sb->s_blocksize = blksize;	/* Using this small block size solve the */
-				/* the misfit with buffer cache and cluster */
-				/* because cluster (DOS) are often aligned */
-				/* on odd sector */
-	sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
-	if (error || debug) {
-		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
-		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
-		    "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(sb)->fat_bits,check,
-		    conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(sb)) ?
-		    ",bmap" : "");
-		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
-		    "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
-		    MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->
-		    fat_length,MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
-		    MSDOS_SB(sb)->data_start,CF_LE_W(*(unsigned short *) &b->
-		    sectors),(unsigned long)b->total_sect,logical_sector_size);
-		printk ("Transaction block size = %d\n",blksize);
-	}
-	if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
-		MSDOS_SB(sb)->clusters = fat_clusters-2;
-	if (error) {
-		if (!silent)
-			printk("VFS: Can't find a valid MSDOS filesystem on dev "
-			       "%s.\n", kdevname(sb->s_dev));
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	sb->s_magic = MSDOS_SUPER_MAGIC;
-	MSDOS_SB(sb)->name_check = check;
-	MSDOS_SB(sb)->conversion = conversion;
-	/* set up enough so that it can read an inode */
-	sb->s_op = &msdos_sops;
-	MSDOS_SB(sb)->fs_uid = uid;
-	MSDOS_SB(sb)->fs_gid = gid;
-	MSDOS_SB(sb)->fs_umask = umask;
-	MSDOS_SB(sb)->quiet = quiet;
-	MSDOS_SB(sb)->dotsOK = dotsOK;
-	MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
-	MSDOS_SB(sb)->fat_wait = NULL;
-	MSDOS_SB(sb)->fat_lock = 0;
-	MSDOS_SB(sb)->prev_free = 0;
-	if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
-		sb->s_dev = 0;
-		printk("get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	return sb;
-}
-
-
-void msdos_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
-{
-	int free,nr;
-	struct statfs tmp;
-
-	lock_fat(sb);
-	if (MSDOS_SB(sb)->free_clusters != -1)
-		free = MSDOS_SB(sb)->free_clusters;
-	else {
-		free = 0;
-		for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
-			if (!fat_access(sb,nr,-1)) free++;
-		MSDOS_SB(sb)->free_clusters = free;
-	}
-	unlock_fat(sb);
-	tmp.f_type = sb->s_magic;
-	tmp.f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
-	tmp.f_blocks = MSDOS_SB(sb)->clusters;
-	tmp.f_bfree = free;
-	tmp.f_bavail = free;
-	tmp.f_files = 0;
-	tmp.f_ffree = 0;
-	tmp.f_namelen = 12;
-	memcpy_tofs(buf, &tmp, bufsiz);
-}
-
-
-int msdos_bmap(struct inode *inode,int block)
-{
-	struct msdos_sb_info *sb;
-	int cluster,offset;
-
-	sb = MSDOS_SB(inode->i_sb);
-	if (inode->i_ino == MSDOS_ROOT_INO) {
-		return sb->dir_start + block;
-	}
-	cluster = block/sb->cluster_size;
-	offset = block % sb->cluster_size;
-	if (!(cluster = get_cluster(inode,cluster))) return 0;
-	return (cluster-2)*sb->cluster_size+sb->data_start+offset;
-}
-
-
-void msdos_read_inode(struct inode *inode)
-{
-	struct super_block *sb = inode->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *raw_entry;
-	int nr;
-
-/* printk("read inode %d\n",inode->i_ino); */
-	MSDOS_I(inode)->i_busy = 0;
-	MSDOS_I(inode)->i_depend = MSDOS_I(inode)->i_old = NULL;
-	MSDOS_I(inode)->i_binary = 1;
-	inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid;
-	inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid;
-	if (inode->i_ino == MSDOS_ROOT_INO) {
-		inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) |
-		    S_IFDIR;
-		inode->i_op = &msdos_dir_inode_operations;
-		inode->i_nlink = msdos_subdirs(inode)+2;
-		    /* subdirs (neither . nor ..) plus . and "self" */
-		inode->i_size = MSDOS_SB(inode->i_sb)->dir_entries*
-		    sizeof(struct msdos_dir_entry);
-		inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*
-		    SECTOR_SIZE;
-		inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
-		    inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
-		MSDOS_I(inode)->i_start = 0;
-		MSDOS_I(inode)->i_attrs = 0;
-		inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
-		return;
-	}
-	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
-	    SECTOR_SIZE))) {
-		printk("dev = %s, ino = %ld\n",
-		       kdevname(inode->i_dev), inode->i_ino);
-		panic("msdos_read_inode: unable to read i-node block");
-	}
-	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-	    [inode->i_ino & (MSDOS_DPB-1)];
-	if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
-		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
-		    ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR;
-		inode->i_op = &msdos_dir_inode_operations;
-		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
-		inode->i_nlink = msdos_subdirs(inode);
-		    /* includes .., compensating for "self" */
-#ifdef DEBUG
-		if (!inode->i_nlink) {
-			printk("directory %d: i_nlink == 0\n",inode->i_ino);
-			inode->i_nlink = 1;
-		}
-#endif
-		inode->i_size = 0;
-		if ((nr = CF_LE_W(raw_entry->start)) != 0)
-			while (nr != -1) {
-				inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->
-				    i_sb)->cluster_size;
-				if (!(nr = fat_access(inode->i_sb,nr,-1))) {
-					printk("Directory %ld: bad FAT\n",
-					    inode->i_ino);
-					break;
-				}
-			}
-	} else { /* not a directory */
-		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
-		    (IS_NOEXEC(inode) ? S_IRUGO|S_IWUGO : S_IRWXUGO)
-		    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG;
-		inode->i_op = sb->s_blocksize == 1024
-			? &msdos_file_inode_operations_1024
-			: &msdos_file_inode_operations;
-		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
-		inode->i_nlink = 1;
-		inode->i_size = CF_LE_L(raw_entry->size);
-	}
-	if(raw_entry->attr & ATTR_SYS)
-		inode->i_flags |= S_IMMUTABLE;
-	MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(inode->i_sb)->conversion,
-	    raw_entry->ext);
-	MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
-	/* this is as close to the truth as we can get ... */
-	inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*SECTOR_SIZE;
-	inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
-	    inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
-	inode->i_mtime = inode->i_atime = inode->i_ctime =
-	    date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
-	brelse(bh);
-}
-
-
-void msdos_write_inode(struct inode *inode)
-{
-	struct super_block *sb = inode->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *raw_entry;
-
-	inode->i_dirt = 0;
-	if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
-	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
-	    SECTOR_SIZE))) {
-		printk("dev = %s, ino = %ld\n",
-		       kdevname(inode->i_dev), inode->i_ino);
-		panic("msdos_write_inode: unable to read i-node block");
-	}
-	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-	    [inode->i_ino & (MSDOS_DPB-1)];
-	if (S_ISDIR(inode->i_mode)) {
-		raw_entry->attr = ATTR_DIR;
-		raw_entry->size = 0;
-	}
-	else {
-		raw_entry->attr = ATTR_NONE;
-		raw_entry->size = CT_LE_L(inode->i_size);
-	}
-	raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
-	    MSDOS_I(inode)->i_attrs;
-	raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start);
-	date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
-	raw_entry->time = CT_LE_W(raw_entry->time);
-	raw_entry->date = CT_LE_W(raw_entry->date);
-	mark_buffer_dirty(bh, 1);
-	brelse(bh);
-}
-
-
-int msdos_notify_change(struct inode * inode,struct iattr * attr)
-{
-	int error;
-
-	error = inode_change_ok(inode, attr);
-	if (error)
-		return error;
-
-	if (((attr->ia_valid & ATTR_UID) && 
-	     (attr->ia_uid != MSDOS_SB(inode->i_sb)->fs_uid)) ||
-	    ((attr->ia_valid & ATTR_GID) && 
-	     (attr->ia_gid != MSDOS_SB(inode->i_sb)->fs_gid)) ||
-	    ((attr->ia_valid & ATTR_MODE) &&
-	     (attr->ia_mode & ~MSDOS_VALID_MODE)))
-		error = -EPERM;
-
-	if (error)
-		return MSDOS_SB(inode->i_sb)->quiet ? 0 : error;
-
-	inode_setattr(inode, attr);
-
-	if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
-		inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
-	else
-		inode->i_mode |= S_IXUGO;
-
-	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
-	    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
-	    ~MSDOS_SB(inode->i_sb)->fs_umask;
-	return 0;
-}
-
-static struct symbol_table msdos_syms = {
-/* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
-#include <linux/symtab_begin.h>
-	/*
-	 * Support for umsdos fs
-	 *
-	 * These symbols are _always_ exported, in case someone
-	 * wants to install the umsdos module later.
-	 */
-	X(msdos_bmap),
-	X(msdos_create),
-	X(msdos_file_read),
-	X(msdos_file_write),
-	X(msdos_lookup),
-	X(msdos_mkdir),
-	X(msdos_mmap),
-	X(msdos_put_inode),
-	X(msdos_put_super),
-	X(msdos_read_inode),
-	X(msdos_read_super),
-	X(msdos_readdir),
-	X(msdos_rename),
-	X(msdos_rmdir),
-	X(msdos_smap),
-	X(msdos_statfs),
-	X(msdos_truncate),
-	X(msdos_unlink),
-	X(msdos_unlink_umsdos),
-	X(msdos_write_inode),
-#include <linux/symtab_end.h>
-};
-
-static struct file_system_type msdos_fs_type = {
-	msdos_read_super, "msdos", 1, NULL
-};
-
-int init_msdos_fs(void)
-{
-	int status;
-
-	if ((status = register_filesystem(&msdos_fs_type)) == 0)
-		status = register_symtab(&msdos_syms);
-	return status;
-}
-
-#ifdef MODULE
-int init_module(void)
-{
-	return init_msdos_fs();
-}
-
-void cleanup_module(void)
-{
-	unregister_filesystem(&msdos_fs_type);
-}
-
-#endif
-
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/misc.c linux/fs/msdos/misc.c
--- v1.3.59/linux/fs/msdos/misc.c	Sat Nov 11 17:41:33 1995
+++ linux/fs/msdos/misc.c	Thu Jan  1 02:00:00 1970
@@ -1,553 +0,0 @@
-/*
- *  linux/fs/msdos/misc.c
- *
- *  Written 1992,1993 by Werner Almesberger
- */
-
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-
-#include "msbuffer.h"
-
-#define PRINTK(x)
-#define Printk(x)	printk x
-
-/* Well-known binary file extensions - of course there are many more */
-
-static char bin_extensions[] =
-  "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */
-  "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z  " "ARJ"	/* common archivers */
-  "TZ " "TAZ" "TZP" "TPZ"		/* abbreviations of tar.Z and tar.zip */
-  "GZ " "TGZ" "DEB"			/* .gz, .tar.gz and Debian packages   */
-  "GIF" "BMP" "TIF" "GL " "JPG" "PCX"	/* graphics */
-  "TFM" "VF " "GF " "PK " "PXL" "DVI";	/* TeX */
-
-
-/*
- * fs_panic reports a severe file system problem and sets the file system
- * read-only. The file system can be made writable again by remounting it.
- */
-
-void fs_panic(struct super_block *s,const char *msg)
-{
-	int not_ro;
-
-	not_ro = !(s->s_flags & MS_RDONLY);
-	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s, ", kdevname(s->s_dev));
-	printk("mounted on %s:%ld)\n  %s\n", /* note: kdevname returns & static char[] */
-	       kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg);
-	if (not_ro)
-		printk("  File system has been set read-only\n");
-}
-
-
-/*
- * is_binary selects optional text conversion based on the conversion mode and
- * the extension part of the file name.
- */
-
-int is_binary(char conversion,char *extension)
-{
-	char *walk;
-
-	switch (conversion) {
-		case 'b':
-			return 1;
-		case 't':
-			return 0;
-		case 'a':
-			for (walk = bin_extensions; *walk; walk += 3)
-				if (!strncmp(extension,walk,3)) return 1;
-			return 0;
-		default:
-			printk("Invalid conversion mode - defaulting to "
-			    "binary.\n");
-			return 1;
-	}
-}
-
-
-/* File creation lock. This is system-wide to avoid deadlocks in rename. */
-/* (rename might deadlock before detecting cross-FS moves.) */
-
-static struct wait_queue *creation_wait = NULL;
-static creation_lock = 0;
-
-
-void lock_creation(void)
-{
-	while (creation_lock) sleep_on(&creation_wait);
-	creation_lock = 1;
-}
-
-
-void unlock_creation(void)
-{
-	creation_lock = 0;
-	wake_up(&creation_wait);
-}
-
-
-void lock_fat(struct super_block *sb)
-{
-	while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait);
-	MSDOS_SB(sb)->fat_lock = 1;
-}
-
-
-void unlock_fat(struct super_block *sb)
-{
-	MSDOS_SB(sb)->fat_lock = 0;
-	wake_up(&MSDOS_SB(sb)->fat_wait);
-}
-
-
-/*
- * msdos_add_cluster tries to allocate a new cluster and adds it to the file
- * represented by inode. The cluster is zero-initialized.
- */
-
-int msdos_add_cluster(struct inode *inode)
-{
-	struct super_block *sb = inode->i_sb;
-	int count,nr,limit,last,curr,sector,last_sector,file_cluster;
-	struct buffer_head *bh;
-	int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size;
-
-	if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
-	if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC;
-	lock_fat(inode->i_sb);
-	limit = MSDOS_SB(inode->i_sb)->clusters;
-	nr = limit; /* to keep GCC happy */
-	for (count = 0; count < limit; count++) {
-		nr = ((count+MSDOS_SB(inode->i_sb)->prev_free) % limit)+2;
-		if (fat_access(inode->i_sb,nr,-1) == 0) break;
-	}
-	PRINTK (("cnt = %d --",count));
-#ifdef DEBUG
-printk("free cluster: %d\n",nr);
-#endif
-	MSDOS_SB(inode->i_sb)->prev_free = (count+MSDOS_SB(inode->i_sb)->
-	    prev_free+1) % limit;
-	if (count >= limit) {
-		MSDOS_SB(inode->i_sb)->free_clusters = 0;
-		unlock_fat(inode->i_sb);
-		return -ENOSPC;
-	}
-	fat_access(inode->i_sb,nr,MSDOS_SB(inode->i_sb)->fat_bits == 12 ?
-	    0xff8 : 0xfff8);
-	if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
-		MSDOS_SB(inode->i_sb)->free_clusters--;
-	unlock_fat(inode->i_sb);
-#ifdef DEBUG
-printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
-#endif
-	last = 0;
-	/* We must locate the last cluster of the file to add this
-	   new one (nr) to the end of the link list (the FAT).
-	   
-	   Here file_cluster will be the number of the last cluster of the
-	   file (before we add nr).
-	   
-	   last is the corresponding cluster number on the disk. We will
-	   use last to plug the nr cluster. We will use file_cluster to
-	   update the cache.
-	*/
-	file_cluster = 0;
-	if ((curr = MSDOS_I(inode)->i_start) != 0) {
-		cache_lookup(inode,INT_MAX,&last,&curr);
-		file_cluster = last;
-		while (curr && curr != -1){
-			PRINTK (("."));
-			file_cluster++;
-			if (!(curr = fat_access(inode->i_sb,
-			    last = curr,-1))) {
-				fs_panic(inode->i_sb,"File without EOF");
-				return -ENOSPC;
-			}
-		}
-		PRINTK ((" --  "));
-	}
-#ifdef DEBUG
-printk("last = %d\n",last);
-#endif
-	if (last) fat_access(inode->i_sb,last,nr);
-	else {
-		MSDOS_I(inode)->i_start = nr;
-		inode->i_dirt = 1;
-	}
-#ifdef DEBUG
-if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
-#endif
-	sector = MSDOS_SB(inode->i_sb)->data_start+(nr-2)*cluster_size;
-	last_sector = sector + cluster_size;
-	for ( ; sector < last_sector; sector++) {
-		#ifdef DEBUG
-			printk("zeroing sector %d\n",sector);
-		#endif
-		if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE)))
-			printk("getblk failed\n");
-		else {
-			memset(bh->b_data,0,SECTOR_SIZE);
-			msdos_set_uptodate(sb,bh,1);
-			mark_buffer_dirty(bh, 1);
-			brelse(bh);
-		}
-	}
-	if (file_cluster != inode->i_blocks/cluster_size){
-		printk ("file_cluster badly computed!!! %d <> %ld\n"
-			,file_cluster,inode->i_blocks/cluster_size);
-	}else{
-		cache_add(inode,file_cluster,nr);
-	}
-	inode->i_blocks += cluster_size;
-	if (S_ISDIR(inode->i_mode)) {
-		if (inode->i_size & (SECTOR_SIZE-1)) {
-			fs_panic(inode->i_sb,"Odd directory size");
-			inode->i_size = (inode->i_size+SECTOR_SIZE) &
-			    ~(SECTOR_SIZE-1);
-		}
-		inode->i_size += SECTOR_SIZE*cluster_size;
-#ifdef DEBUG
-printk("size is %d now (%x)\n",inode->i_size,inode);
-#endif
-		inode->i_dirt = 1;
-	}
-	return 0;
-}
-
-
-/* Linear day numbers of the respective 1sts in non-leap years. */
-
-static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
-		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
-
-
-extern struct timezone sys_tz;
-
-
-/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
-
-int date_dos2unix(unsigned short time,unsigned short date)
-{
-	int month,year,secs;
-
-	month = ((date >> 5) & 15)-1;
-	year = date >> 9;
-	secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
-	    ((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 */
-	secs += sys_tz.tz_minuteswest*60;
-	return secs;
-}
-
-
-/* Convert linear UNIX date to a MS-DOS time/date pair. */
-
-void date_unix2dos(int unix_date,unsigned short *time,
-    unsigned short *date)
-{
-	int day,year,nl_day,month;
-
-	unix_date -= sys_tz.tz_minuteswest*60;
-	*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
-	    (((unix_date/3600) % 24) << 11);
-	day = unix_date/86400-3652;
-	year = day/365;
-	if ((year+3)/4+365*year > day) year--;
-	day -= (year+3)/4+365*year;
-	if (day == 59 && !(year & 3)) {
-		nl_day = day;
-		month = 2;
-	}
-	else {
-		nl_day = (year & 3) || day <= 59 ? day : day-1;
-		for (month = 0; month < 12; month++)
-			if (day_n[month] > nl_day) break;
-	}
-	*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
-}
-
-
-/* Returns the inode number of the directory entry at offset pos. If bh is
-   non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
-   returned in bh. */
-
-int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
-    struct msdos_dir_entry **de)
-{
-	struct super_block *sb = dir->i_sb;
-	int sector,offset;
-
-	while (1) {
-		offset = *pos;
-		PRINTK (("get_entry offset %d\n",offset));
-		if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1)
-			return -1;
-		PRINTK (("get_entry sector %d %p\n",sector,*bh));
-		if (!sector)
-			return -1; /* beyond EOF */
-		*pos += sizeof(struct msdos_dir_entry);
-		if (*bh)
-			brelse(*bh);
-		PRINTK (("get_entry sector apres brelse\n"));
-		if (!(*bh = bread(dir->i_dev,sector,SECTOR_SIZE))) {
-			printk("Directory sread (sector %d) failed\n",sector);
-			continue;
-		}
-		PRINTK (("get_entry apres sread\n"));
-		*de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
-		    (SECTOR_SIZE-1)));
-		return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
-		    MSDOS_DIR_BITS);
-	}
-}
-
-
-/*
- * Now an ugly part: this set of directory scan routines works on clusters
- * rather than on inodes and sectors. They are necessary to locate the '..'
- * directory "inode". raw_scan_sector operates in four modes:
- *
- * name     number   ino      action
- * -------- -------- -------- -------------------------------------------------
- * non-NULL -        X        Find an entry with that name
- * NULL     non-NULL non-NULL Find an entry whose data starts at *number
- * NULL     non-NULL NULL     Count subdirectories in *number. (*)
- * NULL     NULL     non-NULL Find an empty entry
- *
- * (*) The return code should be ignored. It DOES NOT indicate success or
- *     failure. *number has to be initialized to zero.
- *
- * - = not used, X = a value is returned unless NULL
- *
- * If res_bh is non-NULL, the buffer is not deallocated but returned to the
- * caller on success. res_de is set accordingly.
- *
- * If cont is non-zero, raw_found continues with the entry after the one
- * res_bh/res_de point to.
- */
-
-
-#define RSS_NAME /* search for name */ \
-    done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
-     !(data[entry].attr & ATTR_VOLUME);
-
-#define RSS_START /* search for start cluster */ \
-    done = !IS_FREE(data[entry].name) && CF_LE_W(data[entry].start) == *number;
-
-#define RSS_FREE /* search for free entry */ \
-    { \
-	done = IS_FREE(data[entry].name); \
-	if (done) { \
-	    inode = iget(sb,sector*MSDOS_DPS+entry); \
-	    if (inode) { \
-	    /* Directory slots of busy deleted files aren't available yet. */ \
-		done = !MSDOS_I(inode)->i_busy; \
-		iput(inode); \
-	    } \
-	} \
-    }
-
-#define RSS_COUNT /* count subdirectories */ \
-    { \
-	done = 0; \
-	if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
-	    (*number)++; \
-    }
-
-static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
-    int *number,int *ino,struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de,char scantype)
-{
-	struct buffer_head *bh;
-	struct msdos_dir_entry *data;
-	struct inode *inode;
-	int entry,start,done;
-
-	if (!(bh = bread(sb->s_dev,sector,SECTOR_SIZE))) return -EIO;
-	data = (struct msdos_dir_entry *) bh->b_data;
-	for (entry = 0; entry < MSDOS_DPS; entry++) {
-/* RSS_COUNT:  if (data[entry].name == name) done=true else done=false. */
-		if (name) {
-			RSS_NAME
-			if (done && scantype) {   /* scantype != SCAN_ANY */
-				done = (data[entry].attr & ATTR_HIDDEN)
-					? (scantype==SCAN_HID)
-					: (scantype==SCAN_NOTHID);
-			}
-		} else {
-			if (!ino) RSS_COUNT
-			else {
-				if (number) RSS_START
-				else RSS_FREE
-			}
-		}
-		if (done) {
-			if (ino) *ino = sector*MSDOS_DPS+entry;
-			start = CF_LE_W(data[entry].start);
-			if (!res_bh) brelse(bh);
-			else {
-				*res_bh = bh;
-				*res_de = &data[entry];
-			}
-			return start;
-		}
-	}
-	brelse(bh);
-	return -ENOENT;
-}
-
-
-/*
- * raw_scan_root performs raw_scan_sector on the root directory until the
- * requested entry is found or the end of the directory is reached.
- */
-
-static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
-    struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
-{
-	int count,cluster;
-
-	for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
-		if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
-		    name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
-	}
-	return -ENOENT;
-}
-
-
-/*
- * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
- * requested entry is found or the end of the directory is reached.
- */
-
-static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
-    int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
-    **res_de,char scantype)
-{
-	int count,cluster;
-
-#ifdef DEBUG
-	printk("raw_scan_nonroot: start=%d\n",start);
-#endif
-	do {
-		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
-			if ((cluster = raw_scan_sector(sb,(start-2)*
-			    MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
-			    count,name,number,ino,res_bh,res_de,scantype)) >= 0)
-				return cluster;
-		}
-		if (!(start = fat_access(sb,start,-1))) {
-			fs_panic(sb,"FAT error");
-			break;
-		}
-#ifdef DEBUG
-	printk("next start: %d\n",start);
-#endif
-	}
-	while (start != -1);
-	return -ENOENT;
-}
-
-
-/*
- * raw_scan performs raw_scan_sector on any sector.
- *
- * NOTE: raw_scan must not be used on a directory that is is the process of
- *       being created.
- */
-
-static int raw_scan(struct super_block *sb, int start, const char *name,
-    int *number, int *ino, struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de, char scantype)
-{
-	if (start) return raw_scan_nonroot
-		(sb,start,name,number,ino,res_bh,res_de,scantype);
-	else return raw_scan_root
-		(sb,name,number,ino,res_bh,res_de,scantype);
-}
-
-
-/*
- * msdos_parent_ino returns the inode number of the parent directory of dir.
- * File creation has to be deferred while msdos_parent_ino is running to
- * prevent renames.
- */
-
-int msdos_parent_ino(struct inode *dir,int locked)
-{
-	static int zero = 0;
-	int error,curr,prev,nr;
-
-	if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
-	if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
-	if (!locked) lock_creation(); /* prevent renames */
-	if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
-	    &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
-		if (!locked) unlock_creation();
-		return curr;
-	}
-	if (!curr) nr = MSDOS_ROOT_INO;
-	else {
-		if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
-		    NULL,NULL,SCAN_ANY)) < 0) {
-			if (!locked) unlock_creation();
-			return prev;
-		}
-		if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
-		    NULL,SCAN_ANY)) < 0) {
-			if (!locked) unlock_creation();
-			return error;
-		}
-	}
-	if (!locked) unlock_creation();
-	return nr;
-}
-
-
-/*
- * msdos_subdirs counts the number of sub-directories of dir. It can be run
- * on directories being created.
- */
-
-int msdos_subdirs(struct inode *dir)
-{
-	int count;
-
-	count = 0;
-	if (dir->i_ino == MSDOS_ROOT_INO)
-		(void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
-	else {
-		if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
-		else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
-		    NULL,&count,NULL,NULL,NULL,SCAN_ANY);
-	}
-	return count;
-}
-
-
-/*
- * Scans a directory for a given file (name points to its formatted name) or
- * for an empty directory slot (name is NULL). Returns an error code or zero.
- */
-
-int msdos_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de,int *ino, char scantype)
-{
-	int res;
-
-	res = (name)
-		? raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
-		    name, NULL, ino, res_bh, res_de, scantype)
-		: raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
-		    NULL, NULL, ino, res_bh, res_de, scantype);
-	return res<0 ? res : 0;
-}
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/mmap.c linux/fs/msdos/mmap.c
--- v1.3.59/linux/fs/msdos/mmap.c	Mon Jan  8 14:10:27 1996
+++ linux/fs/msdos/mmap.c	Thu Jan  1 02:00:00 1970
@@ -1,113 +0,0 @@
-/*
- *	fs/msdos/mmap.c
- *
- *	Written by Jacques Gelinas (jacques@solucorp.qc.ca)
- *	Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
- *
- *	msdos mmap handling
- */
-
-#include <linux/stat.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/shm.h>
-#include <linux/errno.h>
-#include <linux/mman.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
-#include <linux/msdos_fs.h>
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
-/*
- * Fill in the supplied page for mmap
- */
-static unsigned long msdos_file_mmap_nopage(
-	struct vm_area_struct * area,
-	unsigned long address,
-	int error_code)
-{
-	struct inode * inode = area->vm_inode;
-	unsigned long page;
-	unsigned int clear;
-	int pos;
-	long gap;	/* distance from eof to pos */
-
-	page = __get_free_page(GFP_KERNEL);
-	if (!page)
-		return page;
-	address &= PAGE_MASK;
-	pos = address - area->vm_start + area->vm_offset;
-
-	clear = 0;
-	gap = inode->i_size - pos;
-	if (gap <= 0){
-		/* mmaping beyond end of file */
-		clear = PAGE_SIZE;
-	}else{
-		int cur_read;
-		int need_read;
-		struct file filp;
-		if (gap < PAGE_SIZE){
-			clear = PAGE_SIZE - gap;
-		}
-		filp.f_reada = 0;
-		filp.f_pos = pos;
-		need_read = PAGE_SIZE - clear;
-		{
-			unsigned long cur_fs = get_fs();
-			set_fs (KERNEL_DS);
-			cur_read = msdos_file_read (inode,&filp,(char*)page
-				,need_read);
-			set_fs (cur_fs);
-		}
-		if (cur_read != need_read){
-			printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
-				,cur_read,need_read);
-		}
-	}
-	if (clear > 0){
-		memset ((char*)page+PAGE_SIZE-clear,0,clear);
-	}
-	return page;
-}
-
-struct vm_operations_struct msdos_file_mmap = {
-	NULL,			/* open */
-	NULL,			/* close */
-	NULL,			/* unmap */
-	NULL,			/* protect */
-	NULL,			/* sync */
-	NULL,			/* advise */
-	msdos_file_mmap_nopage,	/* nopage */
-	NULL,			/* wppage */
-	NULL,			/* swapout */
-	NULL,			/* swapin */
-};
-
-/*
- * This is used for a general mmap of an msdos file
- * Returns 0 if ok, or a negative error code if not.
- */
-int msdos_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
-{
-	if (vma->vm_flags & VM_SHARED)	/* only PAGE_COW or read-only supported now */
-		return -EINVAL;
-	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
-		return -EINVAL;
-	if (!inode->i_sb || !S_ISREG(inode->i_mode))
-		return -EACCES;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
-
-	vma->vm_inode = inode;
-	inode->i_count++;
-	vma->vm_ops = &msdos_file_mmap;
-	return 0;
-}
-
-
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/msbuffer.h linux/fs/msdos/msbuffer.h
--- v1.3.59/linux/fs/msdos/msbuffer.h	Tue Jan 31 09:36:21 1995
+++ linux/fs/msdos/msbuffer.h	Thu Jan  1 02:00:00 1970
@@ -1,34 +0,0 @@
-/* buffer.c 13/12/94 20.19.10 */
-struct buffer_head *msdos_bread (struct super_block *sb, int block);
-struct buffer_head *msdos_getblk (struct super_block *sb, int block);
-void msdos_brelse (struct super_block *sb, struct buffer_head *bh);
-void msdos_mark_buffer_dirty (struct super_block *sb,
-	 struct buffer_head *bh,
-	 int dirty_val);
-void msdos_set_uptodate (struct super_block *sb,
-	 struct buffer_head *bh,
-	 int val);
-int msdos_is_uptodate (struct super_block *sb, struct buffer_head *bh);
-void msdos_ll_rw_block (struct super_block *sb, int opr,
-	int nbreq, struct buffer_head *bh[32]);
-
-/* These macros exist to avoid modifying all the code */
-/* They should be removed one day I guess */
-
-/* The versioning mechanism of the modules system define those macros */
-/* This remove some warnings */
-#ifdef brelse
-	#undef brelse
-#endif
-#ifdef bread
-	#undef bread
-#endif
-#ifdef getblk
-	#undef getblk
-#endif
-
-#define brelse(b)				msdos_brelse(sb,b)
-#define bread(d,b,s)			msdos_bread(sb,b)
-#define getblk(d,b,s)			msdos_getblk(sb,b)
-#define mark_buffer_dirty(b,v)	msdos_mark_buffer_dirty(sb,b,v)
-
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v1.3.59/linux/fs/msdos/msdosfs_syms.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/msdos/msdosfs_syms.c	Wed Feb  7 09:39:28 1996
@@ -0,0 +1,37 @@
+#include <linux/module.h>
+
+#include <linux/msdos_fs.h>
+
+static struct symbol_table msdos_syms = {
+#include <linux/symtab_begin.h>
+	/*
+	 * Support for umsdos fs
+	 *
+	 * These symbols are _always_ exported, in case someone
+	 * wants to install the umsdos module later.
+	 */
+  	X(msdos_create),
+  	X(msdos_lookup),
+  	X(msdos_mkdir),
+  	X(msdos_read_inode),
+  	X(msdos_rename),
+  	X(msdos_rmdir),
+  	X(msdos_unlink),
+  	X(msdos_unlink_umsdos),
+#include <linux/symtab_end.h>
+};                                           
+
+struct file_system_type msdos_fs_type = {
+	msdos_read_super, "msdos", 1, NULL
+};
+
+
+int init_msdos_fs(void)
+{
+	int status;
+
+	if ((status = register_filesystem(&msdos_fs_type)) == 0)
+		status = register_symtab(&msdos_syms);
+	return status;
+}
+
diff -u --recursive --new-file v1.3.59/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v1.3.59/linux/fs/msdos/namei.c	Sun Dec 17 11:43:20 1995
+++ linux/fs/msdos/namei.c	Wed Feb  7 09:39:28 1996
@@ -1,10 +1,13 @@
 /*
- *  linux/fs/msdos/namei.c
+ *  linux/fs/msdos/msdos.c
  *
  *  Written 1992,1993 by Werner Almesberger
  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
  */
 
+#define __NO_VERSION__
+#include <linux/module.h>
+
 #include <linux/sched.h>
 #include <linux/msdos_fs.h>
 #include <linux/kernel.h>
@@ -14,10 +17,11 @@
 
 #include <asm/segment.h>
 
-#include "msbuffer.h"
+#include "../fat/msbuffer.h"
 
 #define PRINTK(x)
 
+
 /* MS-DOS "device special files" */
 
 static const char *reserved_names[] = {
@@ -33,6 +37,41 @@
 static char bad_if_strict[] = "+=,; ";
 
 
+void msdos_put_super(struct super_block *sb)
+{
+	fat_put_super(sb);
+	MOD_DEC_USE_COUNT;
+}
+
+struct super_operations msdos_sops = { 
+	msdos_read_inode,
+	fat_notify_change,
+	fat_write_inode,
+	fat_put_inode,
+	msdos_put_super,
+	NULL, /* added in 0.96c */
+	fat_statfs,
+	NULL
+};
+
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
+{
+	struct super_block *res;
+
+	MOD_INC_USE_COUNT;
+
+	sb->s_op = &msdos_sops;
+	res =  fat_read_super(sb, data, silent);
+	if (res == NULL)
+		MOD_DEC_USE_COUNT;
+
+	return res;
+}
+
+
+
+
+
 /***** Formats an MS-DOS file name. Rejects invalid names. */
 static int msdos_format_name(char conv,const char *name,int len,
 	char *res,int dot_dirs,char dotsOK)
@@ -130,7 +169,7 @@
 	} else {
 	    scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
 	}
-	return msdos_scan(dir,msdos_name,bh,de,ino,scantype);
+	return fat_scan(dir,msdos_name,bh,de,ino,scantype);
 }
 
 /***** Get inode using directory and name */
@@ -157,7 +196,7 @@
 		return 0;
 	}
 	if (len == 2 && name[0] == '.' && name[1] == '.') {
-		ino = msdos_parent_ino(dir,0);
+		ino = fat_parent_ino(dir,0);
 		iput(dir);
 		if (ino < 0) return ino;
 		if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
@@ -193,7 +232,7 @@
 		next = MSDOS_I(*result)->i_old;
 		iput(*result);
 		if (!(*result = iget(next->i_sb,next->i_ino))) {
-			fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
+			fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
 			iput(dir);
 			return -ENOENT;
 		}
@@ -214,11 +253,11 @@
 	struct msdos_dir_entry *de;
 	int res,ino;
 
-	if ((res = msdos_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
+	if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
 		if (res != -ENOENT) return res;
 		if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
-		if ((res = msdos_add_cluster(dir)) < 0) return res;
-		if ((res = msdos_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
+		if ((res = fat_add_cluster(dir)) < 0) return res;
+		if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
 	}
 	/*
 	 * XXX all times should be set by caller upon successful completion.
@@ -230,7 +269,7 @@
 	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 	de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
 	de->start = 0;
-	date_unix2dos(dir->i_mtime,&de->time,&de->date);
+	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
 	de->size = 0;
 	mark_buffer_dirty(bh, 1);
 	if ((*result = iget(dir->i_sb,ino)) != NULL)
@@ -260,25 +299,25 @@
 		return res;
 	}
 	is_hid = (name[0]=='.') && (msdos_name[0]!='.');
-	lock_creation();
+	fat_lock_creation();
 	/* Scan for existing file twice, so that creating a file fails
 	 * with -EINVAL if the other (dotfile/nondotfile) exists.
 	 * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
 	 */
-	if (msdos_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
-		unlock_creation();
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
+		fat_unlock_creation();
 		brelse(bh);
 		iput(dir);
 		return is_hid ? -EEXIST : -EINVAL;
  	}
-	if (msdos_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
-		unlock_creation();
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
+		fat_unlock_creation();
 		brelse(bh);
 		iput(dir);
 		return is_hid ? -EINVAL : -EEXIST;
  	}
 	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,result);
-	unlock_creation();
+	fat_unlock_creation();
 	iput(dir);
 	return res;
 }
@@ -303,65 +342,6 @@
 
 #endif
 
-/***** Make a directory */
-int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
-{
-	struct super_block *sb = dir->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	struct inode *inode,*dot;
-	char msdos_name[MSDOS_NAME];
-	int ino,res,is_hid;
-
-	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
-	    msdos_name,0,MSDOS_SB(dir->i_sb)->dotsOK)) < 0) {
-		iput(dir);
-		return res;
-	}
-	is_hid = (name[0]=='.') && (msdos_name[0]!='.');
-	lock_creation();
-	if (msdos_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
-		unlock_creation();
-		brelse(bh);
-		iput(dir);
-		return -EEXIST;
- 	}
-	if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,&inode)) < 0) {
-		unlock_creation();
-		iput(dir);
-		return res;
-	}
-	dir->i_nlink++;
-	inode->i_nlink = 2; /* no need to mark them dirty */
-	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
-	if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error;
-	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
-	MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
-	dot->i_nlink = inode->i_nlink;
-	dot->i_dirt = 1;
-	iput(dot);
-	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	unlock_creation();
-	dot->i_size = dir->i_size;
-	MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
-	dot->i_nlink = dir->i_nlink;
-	dot->i_dirt = 1;
-	MSDOS_I(inode)->i_busy = 0;
-	iput(dot);
-	iput(inode);
-	iput(dir);
-	return 0;
-mkdir_error:
-	iput(inode);
-	if (msdos_rmdir(dir,name,len) < 0)
-		fs_panic(dir->i_sb,"rmdir in mkdir failed");
-	unlock_creation();
-	return res;
-}
-
 /***** See if directory is empty */
 static int msdos_empty(struct inode *dir)
 {
@@ -375,7 +355,7 @@
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
-		while (msdos_get_entry(dir,&pos,&bh,&de) > -1)
+		while (fat_get_entry(dir,&pos,&bh,&de) > -1)
 			if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
 			    MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
 			    MSDOS_NAME)) {
@@ -427,6 +407,65 @@
 	return res;
 }
 
+/***** Make a directory */
+int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	struct inode *inode,*dot;
+	char msdos_name[MSDOS_NAME];
+	int ino,res,is_hid;
+
+	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
+	    msdos_name,0,MSDOS_SB(dir->i_sb)->dotsOK)) < 0) {
+		iput(dir);
+		return res;
+	}
+	is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+	fat_lock_creation();
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
+		fat_unlock_creation();
+		brelse(bh);
+		iput(dir);
+		return -EEXIST;
+ 	}
+	if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,&inode)) < 0) {
+		fat_unlock_creation();
+		iput(dir);
+		return res;
+	}
+	dir->i_nlink++;
+	inode->i_nlink = 2; /* no need to mark them dirty */
+	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+	if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
+	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
+		goto mkdir_error;
+	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
+	MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
+	dot->i_nlink = inode->i_nlink;
+	dot->i_dirt = 1;
+	iput(dot);
+	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
+		goto mkdir_error;
+	fat_unlock_creation();
+	dot->i_size = dir->i_size;
+	MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
+	dot->i_nlink = dir->i_nlink;
+	dot->i_dirt = 1;
+	MSDOS_I(inode)->i_busy = 0;
+	iput(dot);
+	iput(inode);
+	iput(dir);
+	return 0;
+mkdir_error:
+	iput(inode);
+	if (msdos_rmdir(dir,name,len) < 0)
+		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+	fat_unlock_creation();
+	return res;
+}
+
 /***** Unlink a file */
 static int msdos_unlinkx(
 	struct inode *dir,
@@ -493,7 +532,7 @@
 	int new_ino,exists,error;
 
 	if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
-	exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
+	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 	if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 		if (exists) brelse(new_bh);
 		return -ENOENT;
@@ -562,7 +601,7 @@
 	if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
 	/* prevent moving directory below itself */
 	while (walk->i_ino != MSDOS_ROOT_INO) {
-		ino = msdos_parent_ino(walk,1);
+		ino = fat_parent_ino(walk,1);
 		iput(walk);
 		if (ino < 0) return ino;
 		if (ino == old_ino) return -EINVAL;
@@ -570,13 +609,13 @@
 	}
 	iput(walk);
 	/* find free spot */
-	while ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
+	while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
 	    SCAN_ANY)) < 0) {
 		if (error != -ENOENT) return error;
-		error = msdos_add_cluster(new_dir);
+		error = fat_add_cluster(new_dir);
 		if (error) return error;
 	}
-	exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
+	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 	if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
 		brelse(free_bh);
 		if (exists) brelse(new_bh);
@@ -636,7 +675,7 @@
 	}
 	msdos_read_inode(free_inode);
 	MSDOS_I(old_inode)->i_busy = 1;
-	cache_inval_inode(old_inode);
+	fat_cache_inval_inode(old_inode);
 	old_inode->i_dirt = 1;
 	old_de->name[0] = DELETED_FLAG;
 	mark_buffer_dirty(old_bh, 1);
@@ -650,7 +689,7 @@
 		brelse(new_bh);
 	}
 	if (S_ISDIR(old_inode->i_mode)) {
-		if ((error = msdos_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+		if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
 		    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
 		if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
 			brelse(dotdot_bh);
@@ -694,18 +733,63 @@
 	    < 0) goto rename_done;
 	is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
 	old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
-	if ((error = msdos_scan(old_dir,old_msdos_name,&old_bh,&old_de,
+	if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
 	    &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
-	lock_creation();
+	fat_lock_creation();
 	if (old_dir == new_dir)
 		error = rename_same_dir(old_dir,old_msdos_name,new_dir,
 		    new_msdos_name,old_bh,old_de,old_ino,is_hid);
 	else error = rename_diff_dir(old_dir,old_msdos_name,new_dir,
 		    new_msdos_name,old_bh,old_de,old_ino,is_hid);
-	unlock_creation();
+	fat_unlock_creation();
 	brelse(old_bh);
 rename_done:
 	iput(old_dir);
 	iput(new_dir);
 	return error;
 }
+
+
+/* The public inode operations for the msdos fs */
+struct inode_operations msdos_dir_inode_operations = {
+	&fat_dir_operations,	/* default directory file-ops */
+	msdos_create,		/* create */
+	msdos_lookup,		/* lookup */
+	NULL,			/* link */
+	msdos_unlink,		/* unlink */
+	NULL,			/* symlink */
+	msdos_mkdir,		/* mkdir */
+	msdos_rmdir,		/* rmdir */
+	NULL,			/* mknod */
+	msdos_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	fat_bmap,		/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+
+void msdos_read_inode(struct inode *inode)
+{
+	fat_read_inode(inode, &msdos_dir_inode_operations);
+}
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_msdos_fs();
+}
+
+
+void cleanup_module(void)
+{
+	unregister_filesystem(&msdos_fs_type);
+}
+
+#endif
+
diff -u --recursive --new-file v1.3.59/linux/fs/namei.c linux/fs/namei.c
--- v1.3.59/linux/fs/namei.c	Mon Jan  8 14:10:27 1996
+++ linux/fs/namei.c	Wed Feb  7 08:55:39 1996
@@ -396,7 +396,17 @@
 		iput(inode);
 		return error;
 	}
-	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+	if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+		/*
+		 * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com>
+		 * Allow opens of Unix domain sockets and FIFOs for write on
+		 * read-only filesystems. Their data does not live on the disk.
+		 *
+		 * If there was something like IS_NODEV(inode) for
+		 * pipes and/or sockets I'd check it here.
+		 */
+	}
+	else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
 		if (IS_NODEV(inode)) {
 			iput(inode);
 			return -EACCES;
diff -u --recursive --new-file v1.3.59/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v1.3.59/linux/fs/ncpfs/ncplib_kernel.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/ncpfs/ncplib_kernel.c	Wed Feb  7 08:55:39 1996
@@ -577,7 +577,7 @@
 
 	*bytes_read = ntohs(ncp_reply_word(server, 0));
 
-	memcpy_tofs(target, ncp_reply_data(server, 2), *bytes_read);
+	memcpy_tofs(target, ncp_reply_data(server, 2+(offset&1)), *bytes_read);
 
 	ncp_unlock_server(server);
 	return 0;
diff -u --recursive --new-file v1.3.59/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v1.3.59/linux/fs/nfs/inode.c	Tue Jan 23 21:15:51 1996
+++ linux/fs/nfs/inode.c	Wed Feb  7 13:36:52 1996
@@ -248,37 +248,34 @@
 	struct nfs_fattr fattr;
 	int error;
 
+	sattr.mode = (unsigned) -1;
 	if (attr->ia_valid & ATTR_MODE) 
 		sattr.mode = attr->ia_mode;
-	else
-		sattr.mode = (unsigned) -1;
 
+	sattr.uid = (unsigned) -1;
 	if (attr->ia_valid & ATTR_UID)
 		sattr.uid = attr->ia_uid;
-	else
-		sattr.uid = (unsigned) -1;
 
+	sattr.gid = (unsigned) -1;
 	if (attr->ia_valid & ATTR_GID)
 		sattr.gid = attr->ia_gid;
-	else
-		sattr.gid = (unsigned) -1;
 
+
+	sattr.size = (unsigned) -1;
 	if (attr->ia_valid & ATTR_SIZE)
 		sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1;
-	else
-		sattr.size = (unsigned) -1;
 
-	if (attr->ia_valid & ATTR_MTIME_SET) {
+	sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
+	if (attr->ia_valid & ATTR_MTIME) {
 		sattr.mtime.seconds = attr->ia_mtime;
 		sattr.mtime.useconds = 0;
-	} else 
-		sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
+	}
 
-	if (attr->ia_valid & ATTR_ATIME_SET) {
+	sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
+	if (attr->ia_valid & ATTR_ATIME) {
 		sattr.atime.seconds = attr->ia_atime;
 		sattr.atime.useconds = 0;
-	} else
-		sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
+	}
 
 	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
 		&sattr, &fattr);
diff -u --recursive --new-file v1.3.59/linux/fs/nfs/sock.c linux/fs/nfs/sock.c
--- v1.3.59/linux/fs/nfs/sock.c	Sun Jan 14 16:30:12 1996
+++ linux/fs/nfs/sock.c	Wed Feb  7 08:55:39 1996
@@ -92,7 +92,7 @@
 			}
 			if ((timeout.init_timeout <<= 1) >= maxtimeo)
 				timeout.init_timeout = maxtimeo;
-		} else if (result < 0 && result != ERESTARTSYS) {
+		} else if (result < 0 && result != -ERESTARTSYS) {
 			printk("NFS: notice message: result = %d.\n", result);
 		}
 	} while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT));
diff -u --recursive --new-file v1.3.59/linux/fs/open.c linux/fs/open.c
--- v1.3.59/linux/fs/open.c	Thu Jan  4 21:54:58 1996
+++ linux/fs/open.c	Wed Feb  7 13:46:58 1996
@@ -68,7 +68,7 @@
 
 	down(&inode->i_sem);
 	newattrs.ia_size = length;
-	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
 	error = notify_change(inode, &newattrs);
 	if (!error) {
 		/* truncate virtual mappings of this file */
@@ -386,7 +386,7 @@
 		newattrs.ia_valid |= ATTR_MODE;
 	}
 	inode->i_dirt = 1;
-	if (inode->i_sb->dq_op) {
+	if (inode->i_sb && inode->i_sb->dq_op) {
 		inode->i_sb->dq_op->initialize(inode, -1);
 		if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
 			return -EDQUOT;
diff -u --recursive --new-file v1.3.59/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.59/linux/fs/proc/array.c	Sun Jan 14 16:30:12 1996
+++ linux/fs/proc/array.c	Wed Feb  7 08:55:40 1996
@@ -782,6 +782,9 @@
 extern int get_dma_list(char *);
 extern int get_cpuinfo(char *);
 extern int get_pci_list(char*);
+#ifdef __SMP_PROF__
+extern int get_smp_prof_list(char *);
+#endif
 
 static int get_root_array(char * page, int type, char **start, off_t offset, int length)
 {
@@ -839,6 +842,10 @@
 #ifdef CONFIG_APM
 		case PROC_APM:
 			return apm_proc(page);
+#endif
+#ifdef __SMP_PROF__
+		case PROC_SMP_PROF:
+			return get_smp_prof_list(page);
 #endif
 		case PROC_CMDLINE:
 			return get_cmdline(page);
diff -u --recursive --new-file v1.3.59/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.59/linux/fs/proc/root.c	Fri Jan 26 01:37:07 1996
+++ linux/fs/proc/root.c	Wed Feb  7 08:55:40 1996
@@ -333,6 +333,12 @@
 		PROC_INTERRUPTS, 10,"interrupts",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 	});
+#ifdef __SMP_PROF__
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_SMP_PROF, 3,"smp",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+#endif 
 	proc_register(&proc_root, &(struct proc_dir_entry) {
 		PROC_FILESYSTEMS, 11,"filesystems",
 		S_IFREG | S_IRUGO, 1, 0, 0,
diff -u --recursive --new-file v1.3.59/linux/fs/read_write.c linux/fs/read_write.c
--- v1.3.59/linux/fs/read_write.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/read_write.c	Wed Feb  7 13:46:32 1996
@@ -138,19 +138,23 @@
 	error = verify_area(VERIFY_READ,buf,count);
 	if (error)
 		return error;
-	down(&inode->i_sem);
-	written = file->f_op->write(inode,file,buf,count);
-	up(&inode->i_sem);
 	/*
 	 * If data has been written to the file, remove the setuid and
-	 * the setgid bits
+	 * the setgid bits. We do it anyway otherwise there is an
+	 * extremely exploitable race - does your OS get it right |->
+	 *
+	 * Set ATTR_FORCE so it will always be changed.
 	 */
-	if (written > 0 && !suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
+	if (!suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
 		struct iattr newattrs;
 		newattrs.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
-		newattrs.ia_valid = ATTR_MODE;
+		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_FORCE;
 		notify_change(inode, &newattrs);
 	}
+
+	down(&inode->i_sem);
+	written = file->f_op->write(inode,file,buf,count);
+	up(&inode->i_sem);
 	return written;
 }
 
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v1.3.59/linux/fs/umsdos/dir.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/umsdos/dir.c	Wed Feb  7 09:39:29 1996
@@ -152,7 +152,7 @@
 		bufk.dirbuf = dirbuf;
 		bufk.filldir = filldir;
 		bufk.count = 0;
-		ret = msdos_readdir(dir,filp,&bufk,umsdos_dir_once);
+		ret = fat_readdir(dir,filp,&bufk,umsdos_dir_once);
 		if (last_f_pos > 0 && filp->f_pos > last_f_pos) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
 		if (u_entry != NULL) u_entry->flags = 0;
 	}else{
@@ -446,7 +446,7 @@
 		memcpy (d->entry->name,name,name_len);
 		d->entry->name[name_len] = '\0';
 		d->entry->name_len = name_len;
-		ret = 1;	/* So msdos_readdir will terminate */
+		ret = 1;	/* So fat_readdir will terminate */
 	}
 	return ret;
 }
@@ -484,7 +484,7 @@
 			filp.f_pos = 0;
 			bufk.entry = entry;
 			bufk.search_ino = inode->i_ino;
-			msdos_readdir (dir,&filp,&bufk,umsdos_dir_search);
+			fat_readdir (dir,&filp,&bufk,umsdos_dir_search);
 			if (bufk.found){
 				ret = 0;
 				inode->u.umsdos_i.i_dir_owner = dir->i_ino;
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v1.3.59/linux/fs/umsdos/emd.c	Thu Nov  9 11:23:52 1995
+++ linux/fs/umsdos/emd.c	Wed Feb  7 09:39:29 1996
@@ -32,7 +32,7 @@
 	int ret;
 	int old_fs = get_fs();	
 	set_fs (KERNEL_DS);
-	ret = msdos_file_read(inode,filp,buf,count);
+	ret = fat_file_read(inode,filp,buf,count);
 	set_fs (old_fs);
 	return ret;
 }
@@ -48,7 +48,7 @@
 	int ret;
 	int old_fs = get_fs();
 	set_fs (KERNEL_DS);
-	ret = msdos_file_write(inode,filp,buf,count);
+	ret = fat_file_write(inode,filp,buf,count);
 	set_fs (old_fs);
 	return ret;
 }
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v1.3.59/linux/fs/umsdos/file.c	Thu Jan  4 21:54:58 1996
+++ linux/fs/umsdos/file.c	Wed Feb  7 09:39:29 1996
@@ -31,7 +31,7 @@
     int count)
 {
 	/* We have to set the access time because msdos don't care */
-	int ret = msdos_file_read(inode,filp,buf,count);
+	int ret = fat_file_read(inode,filp,buf,count);
 	if (!IS_RDONLY(inode)){
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
@@ -47,7 +47,7 @@
 	const char *buf,
     int count)
 {
-	return msdos_file_write(inode,filp,buf,count);
+	return fat_file_write(inode,filp,buf,count);
 }
 /*
 	Truncate a file to 0 length.
@@ -55,7 +55,7 @@
 static void UMSDOS_truncate(struct inode *inode)
 {
 	PRINTK (("UMSDOS_truncate\n"));
-	msdos_truncate (inode);
+	fat_truncate (inode);
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	inode->i_dirt = 1;
 }
@@ -89,10 +89,10 @@
 	NULL,			/* follow_link */
 	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
-	msdos_bmap,		/* bmap */
+	fat_bmap,		/* bmap */
 	UMSDOS_truncate,/* truncate */
 	NULL,			/* permission */
-	msdos_smap		/* smap */
+	fat_smap		/* smap */
 };
 /* For other with larger and unaligned file system */
 struct file_operations umsdos_file_operations_no_bmap = {
@@ -102,7 +102,7 @@
 	NULL,				/* readdir - bad */
 	NULL,				/* select - default */
 	NULL,				/* ioctl - default */
-	msdos_mmap,			/* mmap */
+	fat_mmap,			/* mmap */
 	NULL,				/* no special open is needed */
 	NULL,				/* release */
 	file_fsync			/* fsync */
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v1.3.59/linux/fs/umsdos/inode.c	Tue Jan 23 21:15:51 1996
+++ linux/fs/umsdos/inode.c	Wed Feb  7 09:39:29 1996
@@ -44,20 +44,20 @@
 	if (inode != NULL && inode == pseudo_root){
 		printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
 	}
-	msdos_put_inode(inode);
+	fat_put_inode(inode);
 }
 
 
 void UMSDOS_put_super(struct super_block *sb)
 {
-	msdos_put_super(sb);
+	fat_put_super(sb);
 	MOD_DEC_USE_COUNT;
 }
 
 
 void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
 {
-	msdos_statfs(sb,buf,bufsiz);
+	fat_statfs(sb,buf,bufsiz);
 }
 
 
@@ -257,7 +257,7 @@
 	struct iattr newattrs;
 
 	PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
-	msdos_write_inode(inode);
+	fat_write_inode(inode);
 	newattrs.ia_mtime = inode->i_mtime;
 	newattrs.ia_atime = inode->i_atime;
 	newattrs.ia_ctime = inode->i_ctime;
@@ -400,11 +400,12 @@
 	*/
 	struct super_block *sb;
 	MOD_INC_USE_COUNT;
-	sb = msdos_read_super(s,data,silent);
+	sb = fat_read_super(s,data,silent);
 	printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n"
 		,UMSDOS_VERSION,UMSDOS_RELEASE);
 	if (sb != NULL){
 		MSDOS_SB(sb)->dotsOK = 0;  /* disable hidden==dotfile */
+		MSDOS_SB(sb)->umsdos = 1;  /* Tell fat-support we're umsdos */
 		sb->s_op = &umsdos_sops;
 		PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
 		umsdos_setup_dir_inode (sb->s_mounted);
@@ -494,13 +495,14 @@
 }
 
 
+
 static struct file_system_type umsdos_fs_type = {
 	UMSDOS_read_super, "umsdos", 1, NULL
 };
 
 int init_umsdos_fs(void)
 {
-        return register_filesystem(&umsdos_fs_type);
+	return register_filesystem(&umsdos_fs_type);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v1.3.59/linux/fs/umsdos/ioctl.c	Thu Nov  9 11:23:52 1995
+++ linux/fs/umsdos/ioctl.c	Wed Feb  7 09:39:29 1996
@@ -59,6 +59,7 @@
 	unsigned long data)
 {
 	int ret = -EPERM;
+	int err;
 	/* #Specification: ioctl / acces
 		Only root (effective id) is allowed to do IOCTL on directory
 		in UMSDOS. EPERM is returned for other user.
@@ -67,8 +68,8 @@
 		Well, not all case require write access, but it simplify the code
 		and let's face it, there is only one client (umssync) for all this
 	*/
-	if (verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
-		ret = -EFAULT;
+	if (err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
+		ret = err;
 	}else if (current->euid == 0
 		|| cmd == UMSDOS_GETVERSION){
 		struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
@@ -118,7 +119,7 @@
 			struct UMSDOS_DIR_ONCE bufk;
 			bufk.count = 0;
 			bufk.ent = &idata->dos_dirent;
-			msdos_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
+			fat_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
 			ret = bufk.count == 1 ? 1 : 0;
 		}else if (cmd == UMSDOS_READDIR_EMD){
 			/* #Specification: ioctl / UMSDOS_READDIR_EMD
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v1.3.59/linux/fs/umsdos/rdir.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/umsdos/rdir.c	Wed Feb  7 09:39:29 1996
@@ -72,7 +72,7 @@
 	bufk.real_root = pseudo_root != NULL
 		&& dir == dir->i_sb->s_mounted
 		&& dir == pseudo_root->i_sb->s_mounted;
-	return msdos_readdir(dir,filp,&bufk,rdir_filldir);
+	return fat_readdir(dir,filp,&bufk,rdir_filldir);
 }
 
 /*
diff -u --recursive --new-file v1.3.59/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v1.3.59/linux/fs/umsdos/symlink.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/umsdos/symlink.c	Wed Feb  7 09:39:29 1996
@@ -103,7 +103,7 @@
 {
 	int ret = -EINVAL;
 	if (S_ISLNK(inode->i_mode)) {
-		ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen);
+		ret = umsdos_readlink_x (inode,buffer,fat_file_read,buflen);
 	}
 	PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
 	iput(inode);
diff -u --recursive --new-file v1.3.59/linux/fs/vfat/Makefile linux/fs/vfat/Makefile
--- v1.3.59/linux/fs/vfat/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/fs/vfat/Makefile	Wed Feb  7 09:39:29 1996
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux vfat-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := vfat.o
+O_OBJS   := namei.o
+OX_OBJS  := 
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.59/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v1.3.59/linux/fs/vfat/namei.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/vfat/namei.c	Wed Feb  7 09:39:29 1996
@@ -0,0 +1,1589 @@
+/*
+ *  linux/fs/msdos/vfat.c
+ *
+ *  Written 1992,1993 by Werner Almesberger
+ *
+ *  Windows95/Windows NT compatible extended MSDOS filesystem
+ *    by Gordon Chaffee Copyright (C) 1995
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include <asm/segment.h>
+
+#include "../fat/msbuffer.h"
+#include "../fat/tables.h"
+
+#if 0
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+
+void vfat_read_inode(struct inode *inode);
+
+
+void vfat_put_super(struct super_block *sb)
+{
+	fat_put_super(sb);
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct super_operations vfat_sops = { 
+	vfat_read_inode,
+	fat_notify_change,
+	fat_write_inode,
+	fat_put_inode,
+	vfat_put_super,
+	NULL, /* added in 0.96c */
+	fat_statfs,
+	NULL
+};
+
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+				    int silent)
+{
+	struct super_block *res;
+  
+	MOD_INC_USE_COUNT;
+	
+	sb->s_op = &vfat_sops;
+	res = fat_read_super(sb, data, silent);
+	if (res == NULL) {
+	  MOD_DEC_USE_COUNT;
+	} else {
+	  MSDOS_SB(sb)->vfat = 1;
+	  MSDOS_SB(sb)->dotsOK = 0;
+	}
+
+	return res;
+}
+
+
+
+#ifdef DEBUG
+
+static void dump_fat(struct super_block *sb,int start)
+{
+	printk("[");
+	while (start) {
+		printk("%d ",start);
+		start = fat_access(sb,start,-1);
+		if (!start) {
+			printk("ERROR");
+			break;
+		}
+		if (start == -1) break;
+	}
+	printk("]\n");
+}
+
+static void dump_de(struct msdos_dir_entry *de)
+{
+	int i;
+	unsigned char *p = (unsigned char *) de;
+	printk("[");
+
+	for (i = 0; i < 32; i++, p++) {
+		printk("%02x ", *p);
+	}
+	printk("]\n");
+}
+
+#endif
+
+/* MS-DOS "device special files" */
+
+static const char *reserved_names[] = {
+	"CON     ","PRN     ","NUL     ","AUX     ",
+	"LPT1    ","LPT2    ","LPT3    ","LPT4    ",
+	"COM1    ","COM2    ","COM3    ","COM4    ",
+	NULL };
+
+
+/* Characters that are undesirable in an MS-DOS file name */
+
+#if 1  /* Steve Searle's characters */
+static char bad_chars[] = "*?<>|\":/\\";
+static char bad_if_strict[] = "+=,; []";
+#else
+static char bad_chars[] = "*?<>|\"";
+static char bad_if_strict[] = "+=,; ";
+#endif
+static char replace_chars[] = "[];,+=";
+
+static int vfat_find(struct inode *dir,const char *name,int len,
+		      int find_long,int new_filename,int is_dir,
+		      struct slot_info *sinfo_out);
+
+/* Checks the validity of an long MS-DOS filename */
+/* Returns negative number on error, 0 for a normal
+ * return, and 1 for . or .. */
+
+static int vfat_valid_longname(const char *name, int len, int dot_dirs)
+{
+	const char **reserved;
+	unsigned char c;
+	int i;
+
+	if (IS_FREE(name)) return -EINVAL;
+	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+		if (!dot_dirs) return -EEXIST;
+		return 1;
+	}
+
+	if (len && name[len-1] == ' ') return -EINVAL;
+	if (len >= 256) return -EINVAL;
+	for (i = 0; i < len; i++) {
+		c = name[i];
+		if (strchr(bad_chars,c)) return -EINVAL;
+	}
+	if (len == 3 || len == 4) {
+		for (reserved = reserved_names; *reserved; reserved++)
+			if (!strncmp(name,*reserved,8)) return -EINVAL;
+	}
+	return 0;
+}
+
+static int vfat_valid_shortname(char conv,const char *name,int len,
+				 int dot_dirs)
+{
+	const char *walk, **reserved;
+	unsigned char c;
+	int space;
+
+	if (IS_FREE(name)) return -EINVAL;
+	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+		if (!dot_dirs) return -EEXIST;
+		return 1;
+	}
+
+	space = 1; /* disallow names starting with a dot */
+	c = 0;
+	for (walk = name; len && walk-name < 8;) {
+	    	c = *walk++;
+		len--;
+		if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+		if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
+		if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
+  		if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+		if ((walk == name) && (c == 0xE5)) c = 0x05;
+		if (c == '.') break;
+		space = c == ' ';
+	}
+	if (space) return -EINVAL;
+	if ((conv == 's' || conv == 'x') && len && c != '.') {
+		c = *walk++;
+		len--;
+		if (c != '.') return -EINVAL;
+	}
+	while (c != '.' && len--) c = *walk++;
+	if (c == '.') {
+		if (len >= 4) return -EINVAL;
+		while (len > 0 && walk-name < (MSDOS_NAME+1)) {
+			c = *walk++;
+			len--;
+			if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+			if (conv == 's' && strchr(bad_if_strict,c))
+				return -EINVAL;
+			if (conv == 'x' && strchr(replace_chars,c))
+				return -EINVAL;
+			if (c < ' ' || c == ':' || c == '\\' || c == '.')
+				return -EINVAL;
+			if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+			space = c == ' ';
+		}
+		if (space) return -EINVAL;
+		if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+	}
+	for (reserved = reserved_names; *reserved; reserved++)
+		if (!strncmp(name,*reserved,8)) return -EINVAL;
+
+	return 0;
+}
+
+/* Takes a short filename and converts it to a formatted MS-DOS filename.
+ * If the short filename is not a valid MS-DOS filename, an error is 
+ * returned.  The formatted short filename is returned in 'res'.
+ */
+
+static int vfat_format_name(char conv,const char *name,int len,char *res,
+  int dot_dirs)
+{
+	char *walk;
+	const char **reserved;
+	unsigned char c;
+	int space;
+
+	if (IS_FREE(name)) return -EINVAL;
+	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+		if (!dot_dirs) return -EEXIST;
+		memset(res+1,' ',10);
+		while (len--) *res++ = '.';
+		return 0;
+	}
+
+	space = 1; /* disallow names starting with a dot */
+	c = 0;
+	for (walk = res; len && walk-res < 8; walk++) {
+	    	c = *name++;
+		len--;
+		if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+		if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
+		if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
+		if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+		if (c == '.') break;
+		space = c == ' ';
+		*walk = c >= 'a' && c <= 'z' ? c-32 : c;
+	}
+	if (space) return -EINVAL;
+	if ((conv == 's' || conv == 'x') && len && c != '.') {
+		c = *name++;
+		len--;
+		if (c != '.') return -EINVAL;
+	}
+	while (c != '.' && len--) c = *name++;
+	if (c == '.') {
+		while (walk-res < 8) *walk++ = ' ';
+		while (len > 0 && walk-res < MSDOS_NAME) {
+			c = *name++;
+			len--;
+			if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+			if (conv == 's' && strchr(bad_if_strict,c))
+				return -EINVAL;
+			if (conv == 'x' && strchr(replace_chars,c))
+				return -EINVAL;
+			if (c < ' ' || c == ':' || c == '\\' || c == '.')
+				return -EINVAL;
+			if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+			space = c == ' ';
+			*walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
+		}
+		if (space) return -EINVAL;
+		if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+	}
+	while (walk-res < MSDOS_NAME) *walk++ = ' ';
+	for (reserved = reserved_names; *reserved; reserved++)
+		if (!strncmp(res,*reserved,8)) return -EINVAL;
+
+	return 0;
+}
+
+static char skip_chars[] = ".:\"?<>| ";
+
+/* Given a valid longname, create a unique shortname.  Make sure the
+ * shortname does not exist
+ */
+static int vfat_create_shortname(struct inode *dir, const char *name,
+     int len, char *name_res)
+{
+	const char *ip, *ext_start, *end;
+	char *p;
+	int valid;
+	int sz, extlen, baselen, totlen;
+	char msdos_name[13];
+	char base[9], ext[4];
+	int i;
+	int res;
+	int ino;
+	int spaces;
+	int count;
+	char buf[8];
+	struct slot_info sinfo;
+	const char *name_start;
+
+	PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
+	sz = 0;			/* Make compiler happy */
+	valid = 1;
+	if (len && name[len-1]==' ') return -EINVAL;
+	if (len <= 12) {
+		/* Do a case insensitive search if the name would be a valid
+		 * shortname if is were all capitalized */
+		for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++)
+		{
+			if (*ip >= 'A' && *ip <= 'Z') {
+				*p = *ip + 32;
+			} else {
+				*p = *ip;
+			}
+		}
+		res = vfat_format_name('x', msdos_name, len, name_res, 1);
+		if (res > -1) {
+			PRINTK(("vfat_create_shortname 1\n"));
+			res = vfat_find(dir, msdos_name, len, 0, 0, 0, &sinfo);
+			ino = sinfo.ino;
+			PRINTK(("vfat_create_shortname 2\n"));
+			if (res > -1) return -EEXIST;
+			return 0;
+		}
+	}
+
+	PRINTK(("vfat_create_shortname 3\n"));
+	/* Now, we need to create a shortname from the long name */
+	ext_start = end = &name[len];
+	while (--ext_start >= name) {
+		if (*ext_start == '.') {
+			if (ext_start == end - 1) {
+				sz = len;
+				ext_start = NULL;
+			}
+			break;
+		}
+	}
+	if (ext_start == name - 1) {
+		sz = len;
+		ext_start = NULL;
+	} else if (ext_start) {
+		/*
+		 * Names which start with a dot could be just
+		 * an extension eg. "...test".  In this case Win95
+		 * uses the extension as the name and sets no extension.
+		 */
+		name_start = &name[0];
+		while (name_start < ext_start)
+		{
+			if (!strchr(skip_chars,*name_start)) break;
+			name_start++;
+		}
+		if (name_start != ext_start) {
+			sz = ext_start - name;
+			ext_start++;
+		} else {
+			sz = len;
+			ext_start=NULL;
+		}
+	}
+
+	for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++)
+	{
+		if (!strchr(skip_chars, *ip)) {
+			if (*ip >= 'A' && *ip <= 'Z') {
+				*p = *ip + 32;
+			} else {
+				*p = *ip;
+			}
+			p++; baselen++;
+		}
+		ip++;
+	}
+	if (baselen == 0) {
+		return -EINVAL;
+	}
+		
+	spaces = 8 - baselen;
+
+	if (ext_start) {
+		extlen = 0;
+		for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
+			if (!strchr(skip_chars, *ip)) {
+				if (*ip >= 'A' && *ip <= 'Z') {
+					*p = *ip + 32;
+				} else {
+					*p = *ip;
+				}
+				if (strchr(replace_chars, *p)) *p='_';
+				extlen++;
+				p++;
+			}
+		}
+	} else {
+		extlen = 0;
+	}
+	ext[extlen] = '\0';
+
+	count = 0;
+	strcpy(msdos_name, base);
+	msdos_name[baselen] = '.';
+	strcpy(&msdos_name[baselen+1], ext);
+
+	totlen = baselen + extlen + 1;
+	res = 0;
+	while (res > -1) {
+		/* Create the next shortname to try */
+		count++;
+		if (count == 10000000) return -EEXIST;
+		sprintf(buf, "%d", count);
+		sz = strlen(buf);
+		if (sz + 1 > spaces) {
+			baselen = baselen - (sz + 1 - spaces);
+			spaces = sz + 1;
+		}
+
+		strncpy(msdos_name, base, baselen);
+		msdos_name[baselen] = '~';
+		strcpy(&msdos_name[baselen+1], buf);
+		msdos_name[baselen+sz+1] = '.';
+		strcpy(&msdos_name[baselen+sz+2], ext);
+
+		totlen = baselen + sz + 2 + extlen;
+		res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
+	}
+	res = vfat_format_name('x', msdos_name, totlen, name_res, 1);
+	return res;
+}
+
+static loff_t vfat_find_free_slots(struct inode *dir,int slots)
+{
+	struct super_block *sb = dir->i_sb;
+	loff_t offset, curr;
+	struct msdos_dir_entry *de;
+	struct buffer_head *bh;
+	struct inode *inode;
+	int ino;
+	int row;
+	int done;
+	int res;
+	int added;
+
+	PRINTK(("vfat_find_free_slots: find %d free slots\n", slots));
+	offset = curr = 0;
+	bh = NULL;
+	row = 0;
+	ino = fat_get_entry(dir,&curr,&bh,&de);
+
+	for (added = 0; added < 2; added++) {
+		while (ino > -1) {
+			done = IS_FREE(de->name);
+			if (done) {
+				inode = iget(sb,ino);
+				if (inode) {
+					/* Directory slots of busy deleted files aren't available yet. */
+					done = !MSDOS_I(inode)->i_busy;
+					/* PRINTK(("inode %d still busy\n", ino)); */
+				}
+				iput(inode);
+			}
+			if (done) {
+				row++;
+				if (row == slots) {
+					brelse(bh);
+					/* printk("----- Free offset at %d\n", offset); */
+					return offset;
+				}
+			} else {
+				row = 0;
+				offset = curr;
+			}
+			ino = fat_get_entry(dir,&curr,&bh,&de);
+		}
+
+		if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+		if ((res = fat_add_cluster(dir)) < 0) return res;
+		ino = fat_get_entry(dir,&curr,&bh,&de);
+	}
+	/* Should never get here, but if it does */
+	printk("vfat_find_free_slots: Unable to find any\n");
+	return -ENOSPC;
+}
+		
+static int vfat_build_slots(struct inode *dir,const char *name,int len,
+     int find_long, int new_filename,
+     struct msdos_dir_slot *ds, struct msdos_dir_slot *ds_mask,
+     int *slots, int *is_long)
+{
+	struct msdos_dir_slot *ps, *ps_mask;
+	struct msdos_dir_entry *de, *de_mask;
+	char msdos_name[MSDOS_NAME];
+	int res;
+	int slot;
+	int i;
+	const unsigned char *ip;
+	loff_t offset;
+	unsigned char alias_checksum;
+
+	PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
+	de = (struct msdos_dir_entry *) ds;
+	de_mask = (struct msdos_dir_entry *) ds_mask;
+
+	*slots = 1;
+	*is_long = 0;
+	memset(ds_mask, 0, sizeof(struct msdos_dir_slot) * MSDOS_SLOTS);
+	if (len == 1 && name[0] == '.') {
+		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
+		memset(de_mask, 0xff, MSDOS_NAME);
+	} else if (len == 2 && name[0] == '.' && name[1] == '.') {
+		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
+		memset(de_mask, 0xff, MSDOS_NAME);
+	} else {
+		PRINTK(("vfat_build_slots 4\n"));
+		res = vfat_valid_shortname('x', name, len, 1);
+		if (res > -1) {
+			PRINTK(("vfat_build_slots 5a\n"));
+			res = vfat_format_name('x', name, len, de->name, 1);
+			PRINTK(("vfat_build_slots 5b\n"));
+			memset(de_mask->name, 0xff, MSDOS_NAME);
+			PRINTK(("vfat_build_slots 5c\n"));
+		} else {
+			PRINTK(("vfat_build_slots 5A: %s (len=%d) is an invalid shortname\n", name, len));
+			if (new_filename) {
+				unsigned char sum;
+
+				PRINTK(("vfat_build_slots 5Z\n"));
+				res = vfat_create_shortname(dir, name, len, msdos_name);
+				PRINTK(("vfat_build_slots 5Y\n"));
+				if (res < 0) {
+					return res;
+				}
+
+				for (sum = i = 0; i < 11; i++) {
+					sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + msdos_name[i];
+				}
+				PRINTK(("vfat_build_slots 5X: msdos_name=%s\n", msdos_name));
+				alias_checksum = sum;
+			} else {
+				alias_checksum = 0;
+			}
+
+			if (!find_long) return -EINVAL;
+			res = vfat_valid_longname(name, len, 1);
+			if (res < 0) return res;
+
+			*is_long = 1;
+			*slots = (len + 12) / 13;
+			PRINTK(("vfat_build_slots 6: slots=%d\n",*slots));
+
+			for (ps = ds, slot = *slots, ps_mask = ds_mask;
+			     slot > 0; slot--, ps++, ps_mask++)
+			{
+				int end, j;
+
+				PRINTK(("vfat_build_slots 6a\n"));
+				ps->id = slot; ps_mask->id = 0xff;
+				ps->attr = ATTR_EXT; ps_mask->attr = 0xff;
+				ps->reserved = 0; ps_mask->reserved = 0xff;
+				ps->alias_checksum = alias_checksum;
+				ps_mask->alias_checksum = 0;
+				ps->start[0] = 0; ps_mask->start[0] = 0xff;
+				ps->start[1] = 0; ps_mask->start[1] = 0xff;
+				PRINTK(("vfat_build_slots 6b: name=%s\n",name));
+				offset = (slot - 1) * 13;
+				ip = &name[offset];
+				j = offset;
+				end = 0;
+				for (i = 0; i < 10; i += 2) {
+					if (!end && j == len) {
+						end = 1;
+						ps->name0_4[i] = 0;
+						ps_mask->name0_4[i] = 0xff;
+						ps->name0_4[i+1] = 0;
+						ps_mask->name0_4[i] = 0xff;
+						continue;
+					} else if (end) {
+						ps->name0_4[i] = 0xff;
+						ps_mask->name0_4[i] = 0xff;
+						ps->name0_4[i+1] = 0xff;
+						ps_mask->name0_4[i+1] = 0xff;
+						continue;
+					}
+					ps->name0_4[i] = fat_a2uni[*ip].uni1;
+					ps->name0_4[i+1] = fat_a2uni[*ip].uni2;
+					if ((*ip >= 'a' && *ip <= 'z') ||
+					    (*ip >= 'A' && *ip <= 'Z')) {
+						ps_mask->name0_4[i] = 0xdf;
+					} else {
+						ps_mask->name0_4[i] = 0xff;
+					}
+					ps_mask->name0_4[i+1] = 0xff;
+					j++; ip++;
+				}
+				PRINTK(("vfat_build_slots 6c\n"));
+				for (i = 0; i < 12; i += 2) {
+					if (!end && j == len) {
+						end = 1;
+						ps->name5_10[i] = 0;
+						ps->name5_10[i+1] = 0;
+						continue;
+					} else if (end) {
+						ps->name5_10[i] = 0xff;
+						ps_mask->name5_10[i] = 0xff;
+						ps->name5_10[i+1] = 0xff;
+						ps_mask->name5_10[i+1] = 0xff;
+						continue;
+					}
+					ps->name5_10[i] = fat_a2uni[*ip].uni1;
+					ps->name5_10[i+1] = fat_a2uni[*ip].uni2;
+					if ((*ip >= 'a' && *ip <= 'z') ||
+					    (*ip >= 'A' && *ip <= 'Z')) {
+						ps_mask->name5_10[i] = 0xdf;
+					} else {
+						ps_mask->name5_10[i] = 0xff;
+					}
+					ps_mask->name5_10[i+1] = 0xff;
+					j++; ip++;
+				}
+				PRINTK(("vfat_build_slots 6d\n"));
+				for (i = 0; i < 4; i += 2) {
+					if (!end && j == len) {
+						end = 1;
+						ps->name11_12[i] = 0;
+						ps->name11_12[i+1] = 0;
+						continue;
+					} else if (end) {
+						ps->name11_12[i] = 0xff;
+						ps_mask->name11_12[i] = 0xff;
+						ps->name11_12[i+1] = 0xff;
+						ps_mask->name11_12[i+1] = 0xff;
+						continue;
+					}
+					ps->name11_12[i] = fat_a2uni[*ip].uni1;
+					ps->name11_12[i+1] = fat_a2uni[*ip].uni2;
+					if ((*ip >= 'a' && *ip <= 'z') ||
+					    (*ip >= 'A' && *ip <= 'Z')) {
+						ps_mask->name11_12[i] = 0xdf;
+					} else {
+						ps_mask->name11_12[i] = 0xff;
+					}
+					ps_mask->name11_12[i+1] = 0xff;
+					j++; ip++;
+				}
+			}
+			PRINTK(("vfat_build_slots 6e\n"));
+			ds[0].id |= 0x40;
+
+			if (new_filename) {
+				de = (struct msdos_dir_entry *) ps;
+				de_mask = (struct msdos_dir_entry *) ps_mask;
+
+				PRINTK(("vfat_build_slots 10\n"));
+				strncpy(de->name, msdos_name, MSDOS_NAME);
+				memset(de_mask->name, 0xff, MSDOS_NAME);
+			}
+		}
+	}
+	return 0;
+}
+
+/* Given a shortname offset, see if there is an associated longname.  Returns
+ * the number of slots in the longname if one is found, else 0 */
+static int vfat_get_longname(struct inode *dir,loff_t short_offset,
+     unsigned char checksum, loff_t *pos_out)
+{
+	struct super_block *sb = dir->i_sb;
+	struct msdos_dir_slot *ps;
+	struct msdos_dir_entry *de;
+	struct buffer_head *bh;
+	loff_t offset, temp;
+	int id, res, slots;
+
+	/* printk("Short offset: %d\n", short_offset); */
+	if (short_offset == 0) {
+		return 0;
+	}
+
+	slots = 0;
+	id = 1;
+	bh = NULL;
+	offset = short_offset - sizeof(struct msdos_dir_slot);
+	while (offset > 0) {
+		temp = offset;
+		res = fat_get_entry(dir,&temp,&bh,&de);
+		if (res < 0) goto finish;
+		ps = (struct msdos_dir_slot *) de;
+		if (ps->alias_checksum != checksum) goto finish;
+		if ((ps->id &~ 0x40) != id) goto finish;
+		if (IS_FREE(de->name)) goto finish;
+		if (ps->id & 0x40) {
+			*pos_out = offset;
+			slots = ps->id &~ 0x40;
+			/* printk("Found a longname for the shortname: long_offset=%ld\n", offset); */
+			goto finish;
+		}
+		offset -= sizeof(struct msdos_dir_slot);
+		id++;
+	}
+ finish:
+	if (bh) brelse(bh);
+	return slots;
+}
+
+static int vfat_find(struct inode *dir,const char *name,int len,
+    int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out)
+{
+	struct super_block *sb = dir->i_sb;
+	const char *ip;
+	char *op, *op_mask;
+	int res;
+	struct msdos_dir_slot ds[MSDOS_SLOTS], ds_mask[MSDOS_SLOTS];
+	struct msdos_dir_slot *ps;
+	struct msdos_dir_entry *de, *de_mask;
+	struct buffer_head *bh;
+	int i, slot, slots;
+	loff_t offset, start;
+	int match;
+	int is_long;
+	unsigned char alias_checksum = 0;
+
+	PRINTK(("vfat_find 1: name=%s, len=%d\n",name,len));
+
+	res = vfat_build_slots(dir, name, len, find_long, new_filename,
+				ds, ds_mask, &slots, &is_long);
+	if (res < 0) return res;
+
+	de = (struct msdos_dir_entry *) ds;
+	de_mask = (struct msdos_dir_entry *) ds_mask;
+
+	PRINTK(("vfat_find 7\n"));
+	offset = start = 0;
+	bh = NULL;
+	sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
+	while (sinfo_out->ino > -1 && slots > 0) {
+		match = 1;
+
+		ps = (struct msdos_dir_slot *) de;
+		alias_checksum = ps->alias_checksum;
+
+		for (slot = 0; slot < slots; slot++) {
+			ip = (char *) de;
+			ps = (struct msdos_dir_slot *) de;
+			if (is_long && ps->alias_checksum != alias_checksum) {
+				printk("Checksums don't match 1\n");
+				match = 0;
+				start = offset;
+				break;
+			}
+
+			for (i = 0, ip = (char *) de, op = (char *) &ds[slot], op_mask = (char *) &ds_mask[slot];
+			     i < sizeof(struct msdos_dir_entry);
+			     i++, ip++, op++, op_mask++)
+			{
+#if 0
+				if (is_long && de->attr == ATTR_EXT)
+					printk("%02x?%02x ",
+					       (unsigned char) *ip,
+					       (unsigned char) *op);
+#endif
+				if ((*ip & *op_mask) != (*op & *op_mask)) {
+					start = offset;
+					match = 0;
+					break;
+				}
+			}
+#if 0
+			if (is_long && de->attr == ATTR_EXT) printk("\n");
+#endif
+			if ((!is_long && !match) ||
+			    (de->attr == ATTR_VOLUME) ||
+			    (is_long && (match || slot == 0)))
+			{
+				sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
+				/* if (ino >=0 && de->attr == ATTR_EXT) dump_de(de); */
+			}
+			if (!match) {
+				break;
+			}
+			if (sinfo_out->ino == -1) {
+				match = 0;
+				goto breakout;
+			}
+		}
+		if (match) {
+			unsigned char sum;
+
+			for (sum = i = 0; i < 11; i++) {
+				sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+			}
+
+			if (is_long) {
+				if (sum != alias_checksum) {
+					PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
+					match = 0;
+				}
+			} else {
+				int long_slots;
+				long_slots = vfat_get_longname(dir, offset - sizeof(struct msdos_dir_entry), sum, &start);
+				if (long_slots > 0) {
+					slots = long_slots;
+					is_long = 1;
+				}
+			}
+
+				
+			if (match) {
+				PRINTK(("name: %s, alias: %c%c%c%c%c%c%c%c%c%c%c\n",
+					name,
+					de->name[0], de->name[1], de->name[2],
+					de->name[3], de->name[4], de->name[5],
+					de->name[6], de->name[7], de->name[8],
+					de->name[9], de->name[10]));
+				PRINTK(("vfat_find 10\n"));
+				res = CF_LE_W(de->start);
+
+				sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
+				sinfo_out->longname_offset = start;
+				sinfo_out->is_long = is_long;
+				if (is_long) {
+					sinfo_out->long_slots = slots;
+					slots++;
+				} else {
+					sinfo_out->long_slots = 0;
+				}
+
+				sinfo_out->total_slots = slots;
+				if (new_filename) {
+					if (bh) brelse(bh);
+					return -EEXIST;
+				}
+				if (bh) brelse(bh);
+				return res;
+			}
+		}
+	}
+ breakout:
+	PRINTK(("breakout\n"));
+
+	if (bh) brelse(bh);
+	if (new_filename) {
+		PRINTK(("vfat_find: create file 1\n"));
+		if (is_long) slots++;
+		offset = vfat_find_free_slots(dir, slots);
+		if (offset < 0) {
+			return offset;
+		}
+
+		PRINTK(("vfat_find: create file 2\n"));
+		/* Now create the new entry */
+		bh = NULL;
+		for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
+			PRINTK(("vfat_find: create file 3, slot=%d\n",slot));
+			sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
+			if (sinfo_out->ino < 0) {
+				PRINTK(("vfat_find: problem\n"));
+				return sinfo_out->ino;
+			}
+			memcpy(de, ps, sizeof(struct msdos_dir_slot));
+			mark_buffer_dirty(bh, 1);
+		}
+
+		PRINTK(("vfat_find: create file 4\n"));
+		dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
+		dir->i_dirt = 1;
+
+		PRINTK(("vfat_find: create file 5\n"));
+
+		memset(de->unused, 0, sizeof(de->unused));
+		fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
+		de->ctime_ms = 0;
+		de->ctime = de->time;
+		de->adate = de->cdate = de->date;
+		de->start = 0;
+		de->size = 0;
+		de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+		de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
+
+
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+
+		sinfo_out->is_long = (slots > 1) ? 1 : 0;
+		if (sinfo_out->is_long) {
+			sinfo_out->long_slots = slots - 1;
+		} else {
+			sinfo_out->long_slots = 0;
+		}
+		sinfo_out->total_slots = slots;
+		sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
+		sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+int vfat_lookup(struct inode *dir,const char *name,int len,
+    struct inode **result)
+{
+	int res, ino;
+	struct inode *next;
+	struct slot_info sinfo;
+	
+	PRINTK (("vfat_lookup: name=%s, len=%d\n", name, len));
+
+	*result = NULL;
+	if (!dir) return -ENOENT;
+	if (!S_ISDIR(dir->i_mode)) {
+		iput(dir);
+		return -ENOENT;
+	}
+	PRINTK (("vfat_lookup 2\n"));
+	if (len == 1 && name[0] == '.') {
+		*result = dir;
+		return 0;
+	}
+	if (len == 2 && name[0] == '.' && name[1] == '.') {
+		ino = fat_parent_ino(dir,0);
+		iput(dir);
+		if (ino < 0) return ino;
+		if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
+		return 0;
+	}
+	PRINTK (("vfat_lookup 3\n"));
+	if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0) {
+		iput(dir);
+		return res;
+	}
+	PRINTK (("vfat_lookup 4.5\n"));
+	if (!(*result = iget(dir->i_sb,sinfo.ino))) {
+		iput(dir);
+		return -EACCES;
+	}
+	PRINTK (("vfat_lookup 5\n"));
+	if (!(*result)->i_sb ||
+	    ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+		/* crossed a mount point into a non-msdos fs */
+		iput(dir);
+		return 0;
+	}
+	if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
+		iput(*result);
+		iput(dir);
+		return -ENOENT;
+	}
+	PRINTK (("vfat_lookup 6\n"));
+	while (MSDOS_I(*result)->i_old) {
+		next = MSDOS_I(*result)->i_old;
+		iput(*result);
+		if (!(*result = iget(next->i_sb,next->i_ino))) {
+			fat_fs_panic(dir->i_sb,"vfat_lookup: Can't happen");
+			iput(dir);
+			return -ENOENT;
+		}
+	}
+	iput(dir);
+	return 0;
+}
+
+
+static int vfat_create_entry(struct inode *dir,const char *name,int len,
+    int is_dir, struct inode **result)
+{
+	struct super_block *sb = dir->i_sb;
+	int res,ino;
+	loff_t offset;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	struct slot_info sinfo;
+
+	PRINTK(("vfat_create_entry 1\n"));
+	res = vfat_find(dir, name, len, 1, 1, is_dir, &sinfo);
+	if (res < 0) {
+		return res;
+	}
+
+	offset = sinfo.shortname_offset;
+
+	PRINTK(("vfat_create_entry 2\n"));
+	bh = NULL;
+	ino = fat_get_entry(dir, &offset, &bh, &de);
+	if (ino < 0) {
+		PRINTK(("vfat_mkdir problem\n"));
+		if (bh) brelse(bh);
+		return ino;
+	}
+	PRINTK(("vfat_create_entry 3\n"));
+
+	if ((*result = iget(dir->i_sb,ino)) != NULL)
+		vfat_read_inode(*result);
+	brelse(bh);
+	if (!*result) return -EIO;
+	(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
+	    CURRENT_TIME;
+	(*result)->i_dirt = 1;
+
+	return 0;
+}
+
+int vfat_create(struct inode *dir,const char *name,int len,int mode,
+	struct inode **result)
+{
+	int res;
+
+	if (!dir) return -ENOENT;
+
+	fat_lock_creation();
+	if ((res = vfat_create_entry(dir,name,len,0,result)) < 0) {
+		printk("vfat_create: unable to get new entry\n");
+		fat_unlock_creation();
+		iput(dir);
+		return res;
+	}
+
+	fat_unlock_creation();
+	iput(dir);
+	return res;
+}
+
+static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
+     struct buffer_head *bh,
+     struct msdos_dir_entry *de,int ino,const char *name, int isdot)
+{
+	struct super_block *sb = dir->i_sb;
+	struct inode *dot;
+
+	PRINTK(("vfat_create_a_dotdir 1\n"));
+
+	/*
+	 * XXX all times should be set by caller upon successful completion.
+	 */
+	dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_dirt = 1;
+	memcpy(de->name,name,MSDOS_NAME);
+	memset(de->unused, 0, sizeof(de->unused));
+	de->lcase = 0;
+	de->attr = ATTR_DIR;
+	de->start = 0;
+	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
+	de->ctime_ms = 0;
+	de->ctime = de->time;
+	de->adate = de->cdate = de->date;
+	de->size = 0;
+	mark_buffer_dirty(bh, 1);
+	if ((dot = iget(dir->i_sb,ino)) != NULL)
+		vfat_read_inode(dot);
+	if (!dot) return -EIO;
+	dot->i_mtime = dot->i_atime = CURRENT_TIME;
+	dot->i_dirt = 1;
+	if (isdot) {
+		dot->i_size = dir->i_size;
+		MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
+		dot->i_nlink = dir->i_nlink;
+	} else {
+		dot->i_size = parent->i_size;
+		MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
+		dot->i_nlink = parent->i_nlink;
+	}
+
+	iput(dot);
+
+	PRINTK(("vfat_create_a_dotdir 2\n"));
+	return 0;
+}
+
+static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
+{
+	struct super_block *sb = dir->i_sb;
+	int res;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	loff_t offset;
+
+	PRINTK(("vfat_create_dotdirs 1\n"));
+	if ((res = fat_add_cluster(dir)) < 0) return res;
+
+	PRINTK(("vfat_create_dotdirs 2\n"));
+	offset = 0;
+	bh = NULL;
+	if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
+	
+	PRINTK(("vfat_create_dotdirs 3\n"));
+	res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
+	PRINTK(("vfat_create_dotdirs 4\n"));
+	if (res < 0) {
+		brelse(bh);
+		return res;
+	}
+	PRINTK(("vfat_create_dotdirs 5\n"));
+
+	if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
+		brelse(bh);
+		return res;
+	}
+	PRINTK(("vfat_create_dotdirs 6\n"));
+
+	res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
+	PRINTK(("vfat_create_dotdirs 7\n"));
+	brelse(bh);
+
+	return res;
+}
+
+/***** See if directory is empty */
+static int vfat_empty(struct inode *dir)
+{
+	struct super_block *sb = dir->i_sb;
+	loff_t pos;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+
+	if (dir->i_count > 1)
+		return -EBUSY;
+	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
+		pos = 0;
+		bh = NULL;
+		while (fat_get_entry(dir,&pos,&bh,&de) > -1)
+			/* Skip extended filename entries */
+			if (de->attr == ATTR_EXT) continue;
+
+			if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
+			    MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
+			    MSDOS_NAME)) {
+				brelse(bh);
+				return -ENOTEMPTY;
+			}
+		if (bh)
+			brelse(bh);
+	}
+	return 0;
+}
+
+static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
+     struct msdos_dir_entry *de,int ino)
+{
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode;
+	int res;
+
+	if (ino < 0) return -EINVAL;
+	if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
+	if (!S_ISDIR(inode->i_mode)) {
+		iput(inode);
+		return -ENOTDIR;
+	}
+	if (dir->i_dev != inode->i_dev || dir == inode) {
+		iput(inode);
+		return -EBUSY;
+	}
+	res = vfat_empty(inode);
+	if (res) {
+		iput(inode);
+		return res;
+	}
+	inode->i_nlink = 0;
+	inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+	inode->i_atime = dir->i_atime = CURRENT_TIME;
+	dir->i_nlink--;
+	inode->i_dirt = dir->i_dirt = 1;
+	de->name[0] = DELETED_FLAG;
+	mark_buffer_dirty(bh, 1);
+	iput(inode);
+
+	return 0;
+}
+
+static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
+     struct msdos_dir_entry *de,int ino,int nospc)
+{
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode;
+	if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
+	if (!S_ISREG(inode->i_mode) && nospc) {
+		iput(inode);
+		return -EPERM;
+	}
+	if (IS_IMMUTABLE(inode)){
+		iput(inode);
+		return -EPERM;
+	}
+	inode->i_nlink = 0;
+	inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+	inode->i_atime = dir->i_atime = CURRENT_TIME;
+	MSDOS_I(inode)->i_busy = 1;
+	inode->i_dirt = dir->i_dirt = 1;
+	de->name[0] = DELETED_FLAG;
+	mark_buffer_dirty(bh, 1);
+
+	iput(inode);
+	return 0;
+}
+
+static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
+     struct buffer_head **bh,struct msdos_dir_entry **de,
+     int is_dir,int nospc)
+{
+	struct super_block *sb = dir->i_sb;
+	loff_t offset;
+	int res, i;
+
+	/* remove the shortname */
+	offset = sinfo->shortname_offset;
+	res = fat_get_entry(dir, &offset, bh, de);
+	if (res < 0) return res;
+	if (is_dir) {
+		res = vfat_rmdir_free_ino(dir,*bh,*de,res);
+	} else {
+		res = vfat_unlink_free_ino(dir,*bh,*de,res,nospc);
+	}
+	if (res < 0) return res;
+		
+	/* remove the longname */
+	offset = sinfo->longname_offset;
+	for (i = sinfo->long_slots; i > 0; --i) {
+		res = fat_get_entry(dir, &offset, bh, de);
+		if (res < 0) {
+			printk("vfat_remove_entry: problem 1\n");
+			continue;
+		}
+		(*de)->name[0] = DELETED_FLAG;
+		(*de)->attr = 0;
+		mark_buffer_dirty(*bh, 1);
+	}
+	return 0;
+}
+
+
+static int vfat_rmdirx(struct inode *dir,const char *name,int len)
+{
+	struct super_block *sb = dir->i_sb;
+	int res;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	struct slot_info sinfo;
+
+	bh = NULL;
+	res = -EPERM;
+	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+		goto rmdir_done;
+	res = vfat_find(dir,name,len,1,0,0,&sinfo);
+
+	if (res >= 0 && sinfo.total_slots > 0) {
+		res = vfat_remove_entry(dir,&sinfo,&bh,&de,1,0);
+		if (res > 0) {
+			res = 0;
+		}
+	} else {
+		printk("Problem in vfat_rmdirx\n");
+	}
+
+rmdir_done:
+	brelse(bh);
+	return res;
+}
+
+/***** Remove a directory */
+int vfat_rmdir(struct inode *dir,const char *name,int len)
+{
+	int res;
+
+	res = vfat_rmdirx(dir, name, len);
+	iput(dir);
+	return res;
+}
+
+static int vfat_unlinkx(
+	struct inode *dir,
+	const char *name,
+	int len,
+	int nospc)	/* Flag special file ? */
+{
+	struct super_block *sb = dir->i_sb;
+	int res;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	struct slot_info sinfo;
+
+	bh = NULL;
+	if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0)
+		goto unlink_done;
+
+	if (res >= 0 && sinfo.total_slots > 0) {
+		res = vfat_remove_entry(dir,&sinfo,&bh,&de,0,nospc);
+		if (res > 0) {
+			res = 0;
+		}
+	} else {
+		printk("Problem in vfat_unlinkx: res=%d, total_slots=%d\n",res, sinfo.total_slots);
+	}
+
+unlink_done:
+	brelse(bh);
+	return res;
+}
+
+
+int vfat_mkdir(struct inode *dir,const char *name,int len,int mode)
+{
+	struct inode *inode;
+	int res;
+
+	fat_lock_creation();
+	if ((res = vfat_create_entry(dir,name,len,1,&inode)) < 0) {
+		fat_unlock_creation();
+		iput(dir);
+		return res;
+	}
+
+	dir->i_nlink++;
+	inode->i_nlink = 2; /* no need to mark them dirty */
+	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+
+	res = vfat_create_dotdirs(inode, dir);
+	fat_unlock_creation();
+	MSDOS_I(inode)->i_busy = 0;
+	iput(inode);
+	iput(dir);
+	if (res < 0) {
+		if (vfat_rmdir(dir,name,len) < 0)
+			fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+	}
+	return res;
+}
+
+/***** Unlink, as called for msdosfs */
+int vfat_unlink(struct inode *dir,const char *name,int len)
+{
+	int res;
+
+	res = vfat_unlinkx (dir,name,len,1);
+	iput(dir);
+	return res;
+}
+
+
+int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
+	struct inode *new_dir,const char *new_name,int new_len)
+{
+	struct super_block *sb = old_dir->i_sb;
+	struct buffer_head *old_bh,*new_bh,*dotdot_bh;
+	struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+	loff_t old_offset,new_offset,old_longname_offset;
+	int old_slots,old_ino,new_ino,dotdot_ino,ino;
+	struct inode *old_inode, *new_inode, *dotdot_inode, *walk;
+	int res, is_dir, i;
+	int locked = 0;
+	struct slot_info sinfo;
+
+	PRINTK(("vfat_rename 1\n"));
+	if (old_dir == new_dir && old_len == new_len &&
+	    strncmp(old_name, new_name, old_len) == 0)
+		return 0;
+
+	old_bh = new_bh = NULL;
+	old_inode = new_inode = NULL;
+	res = vfat_find(old_dir,old_name,old_len,1,0,0,&sinfo);
+	PRINTK(("vfat_rename 2\n"));
+	if (res < 0) goto rename_done;
+
+	old_slots = sinfo.total_slots;
+	old_longname_offset = sinfo.longname_offset;
+	old_offset = sinfo.shortname_offset;
+	old_ino = sinfo.ino;
+	res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
+	PRINTK(("vfat_rename 3\n"));
+	if (res < 0) goto rename_done;
+
+	if (!(old_inode = iget(old_dir->i_sb,old_ino))) goto rename_done;
+	is_dir = S_ISDIR(old_inode->i_mode);
+	if (is_dir) {
+		if ((old_dir->i_dev != new_dir->i_dev) ||
+		    (old_ino == new_dir->i_ino)) {
+			res = -EINVAL;
+			goto rename_done;
+		}
+		if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+		/* prevent moving directory below itself */
+		while (walk->i_ino != MSDOS_ROOT_INO) {
+			ino = fat_parent_ino(walk,1);
+			iput(walk);
+			if (ino < 0) return ino;
+			if (ino == old_ino) return -EINVAL;
+			if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
+		}
+		iput(walk);
+	}
+
+	res = vfat_find(new_dir,new_name,new_len,1,0,is_dir,&sinfo);
+
+	PRINTK(("vfat_rename 4\n"));
+	if (res > -1) {
+		int new_is_dir;
+
+		PRINTK(("vfat_rename 5\n"));
+		/* Filename currently exists.  Need to delete it */
+		new_offset = sinfo.shortname_offset;
+		res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
+		PRINTK(("vfat_rename 6\n"));
+		if (res < 0) goto rename_done;
+
+		if (!(new_inode = iget(new_dir->i_sb,res)))
+			goto rename_done;
+		new_is_dir = S_ISDIR(new_inode->i_mode);
+		iput(new_inode);
+		if (new_is_dir) {
+			PRINTK(("vfat_rename 7\n"));
+			res = vfat_rmdirx(new_dir,new_name,new_len);
+			PRINTK(("vfat_rename 8\n"));
+			if (res < 0) goto rename_done;
+		} else {
+			PRINTK(("vfat_rename 9\n"));
+			res = vfat_unlinkx(new_dir,new_name,new_len,1);
+			PRINTK(("vfat_rename 10\n"));
+			if (res < 0) goto rename_done;
+		}
+	}
+
+	PRINTK(("vfat_rename 11\n"));
+	fat_lock_creation(); locked = 1;
+	res = vfat_find(new_dir,new_name,new_len,1,1,is_dir,&sinfo);
+
+	PRINTK(("vfat_rename 12\n"));
+	if (res < 0) goto rename_done;
+
+	new_offset = sinfo.shortname_offset;
+	new_ino = sinfo.ino;
+	res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
+	PRINTK(("vfat_rename 13\n"));
+	if (res < 0) goto rename_done;
+
+	new_de->attr = old_de->attr;
+	new_de->time = old_de->time;
+	new_de->date = old_de->date;
+	new_de->ctime_ms = old_de->ctime_ms;
+	new_de->cdate = old_de->cdate;
+	new_de->adate = old_de->adate;
+	new_de->start = old_de->start;
+	new_de->size = old_de->size;
+
+	if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
+	PRINTK(("vfat_rename 14\n"));
+
+	/* At this point, we have the inodes of the old file and the
+	 * new file.  We need to transfer all information from the old
+	 * inode to the new inode and then delete the slots of the old
+	 * entry
+	 */
+
+	vfat_read_inode(new_inode);
+	MSDOS_I(old_inode)->i_busy = 1;
+	fat_cache_inval_inode(old_inode);
+	PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots));
+	old_inode->i_dirt = 1;
+
+	/* remove the old entry */
+	for (i = old_slots; i > 0; --i) {
+		res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
+		if (res < 0) {
+			printk("vfat_unlinkx: problem 1\n");
+			continue;
+		}
+		old_de->name[0] = DELETED_FLAG;
+		old_de->attr = 0;
+		mark_buffer_dirty(old_bh, 1);
+	}
+	PRINTK(("vfat_rename 15b\n"));
+
+	mark_buffer_dirty(new_bh, 1);
+	iput(new_inode);
+	/* XXX: There is some code in the original MSDOS rename that
+	 * is not duplicated here and it might cause a problem in
+	 * certain circumstances.
+	 */
+	
+	if (S_ISDIR(old_inode->i_mode)) {
+		if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+		    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
+		if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
+			brelse(dotdot_bh);
+			res = -EIO;
+			goto rename_done;
+		}
+		dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
+		    MSDOS_I(new_dir)->i_start;
+		dotdot_inode->i_dirt = 1;
+		mark_buffer_dirty(dotdot_bh, 1);
+		old_dir->i_nlink--;
+		new_dir->i_nlink++;
+		/* no need to mark them dirty */
+		dotdot_inode->i_nlink = new_dir->i_nlink;
+		iput(dotdot_inode);
+		brelse(dotdot_bh);
+	}
+
+	if (res > 0) res = 0;
+
+rename_done:
+	if (locked) fat_unlock_creation();
+	if (old_bh) brelse(old_bh);
+	if (new_bh) brelse(new_bh);
+	if (old_inode) iput(old_inode);
+	iput(old_dir);
+	iput(new_dir);
+	return res;
+}
+
+
+
+/* Public inode operations for the VFAT fs */
+struct inode_operations vfat_dir_inode_operations = {
+	&fat_dir_operations,	/* default directory file-ops */
+	vfat_create,		/* create */
+	vfat_lookup,		/* lookup */
+	NULL,			/* link */
+	vfat_unlink,		/* unlink */
+	NULL,			/* symlink */
+	vfat_mkdir,		/* mkdir */
+	vfat_rmdir,		/* rmdir */
+	NULL,			/* mknod */
+	vfat_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	fat_bmap,		/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+
+void vfat_read_inode(struct inode *inode)
+{
+	fat_read_inode(inode, &vfat_dir_inode_operations);
+}
+
+
+
+
+static struct file_system_type vfat_fs_type = {
+	vfat_read_super, "vfat", 1, NULL
+};
+
+int init_vfat_fs(void)
+{
+	int status;
+
+	if ((status = register_filesystem(&vfat_fs_type)) == 0)
+		status = register_symtab(0);
+	return status;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_vfat_fs();
+}
+
+
+void cleanup_module(void)
+{
+	unregister_filesystem(&vfat_fs_type);
+}
+
+#endif /* ifdef MODULE */
+
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.59/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v1.3.59/linux/include/asm-alpha/system.h	Mon Jan  8 14:10:28 1996
+++ linux/include/asm-alpha/system.h	Wed Feb  7 07:48:33 1996
@@ -50,7 +50,7 @@
 extern unsigned long rdmces (void);
 extern void wrmces (unsigned long);
 
-#define halt() __asm__ __volatile__(".long 0");
+#define halt() __asm__ __volatile__ ("call_pal %0" : : "i" (PAL_halt) : "memory")
 
 extern void alpha_switch_to(unsigned long pctxp);
 
diff -u --recursive --new-file v1.3.59/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v1.3.59/linux/include/asm-i386/irq.h	Mon Nov 13 12:36:46 1995
+++ linux/include/asm-i386/irq.h	Wed Feb  7 08:55:40 1996
@@ -129,6 +129,14 @@
 
 #ifdef	__SMP__
 
+#ifndef __SMP_PROF__
+#define SMP_PROF_INT_SPINS 
+#define SMP_PROF_IPI_CNT 
+#else
+#define SMP_PROF_INT_SPINS "incl "SYMBOL_NAME_STR(smp_spins)"(,%eax,4)\n\t"
+#define SMP_PROF_IPI_CNT "incl "SYMBOL_NAME_STR(ipi_count)"\n\t" 
+#endif
+
 #define GET_PROCESSOR_ID \
 	"movl "SYMBOL_NAME_STR(apic_reg)", %edx\n\t" \
 	"movl 32(%edx), %eax\n\t" \
@@ -148,7 +156,7 @@
 	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
 	"je 4f\n\t" \
 	"2: " \
-	"incl "SYMBOL_NAME_STR(smp_spins)"\n\t" \
+        SMP_PROF_INT_SPINS \
 	"btl %al, "SYMBOL_NAME_STR(smp_invalidate_needed)"\n\t" \
 	"jnc 5f\n\t" \
 	"lock\n\t" \
@@ -261,7 +269,7 @@
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
 	SAVE_MOST \
 	ACK_##chip(mask) \
-	"incl "SYMBOL_NAME_STR(ipi_count)"\n\t" \
+	SMP_PROF_IPI_CNT \
 	"pushl $" #nr "\n\t" \
 	"call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
 	"addl $4,%esp\n\t" \
diff -u --recursive --new-file v1.3.59/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v1.3.59/linux/include/asm-i386/pgtable.h	Thu Jan  4 21:54:58 1996
+++ linux/include/asm-i386/pgtable.h	Wed Feb  7 08:55:40 1996
@@ -79,6 +79,59 @@
 #define local_invalidate() \
 	__invalidate()
 
+
+#undef CLEVER_SMP_INVALIDATE
+#ifdef CLEVER_SMP_INVALIDATE
+
+/*
+ *	Smarter SMP invalidation macros. 
+ *		c/o Linus Torvalds.
+ *
+ *	These mean you can really definitely utterly forget about
+ *	writing to user space from interrupts. (Its not allowed anyway).
+ *
+ *	Doesn't currently work as Linus makes invalidate calls before
+ *	stuff like current/current->mm are setup properly
+ */
+ 
+static inline void invalidate_current_task(void)
+{
+	if (current->mm->count == 1)	/* just one copy of this mm */
+		local_invalidate();	/* and that's us, so.. */
+	else
+		smp_invalidate();
+}
+
+#define invalidate() invalidate_current_task()
+
+#define invalidate_all() smp_invalidate()
+
+static inline void invalidate_mm(struct mm_struct * mm)
+{
+	if (mm == current->mm && mm->count == 1)
+		local_invalidate();
+	else
+		smp_invalidate();
+}
+
+static inline void invalidate_page(struct vm_area_struct * vma,
+	unsigned long va)
+{
+	if (vma->vm_mm == current->mm && current->mm->count == 1)
+		__invalidate_one(va);
+	else
+		smp_invalidate();
+}
+
+static inline void invalidate_range(struct mm_struct * mm,
+	unsigned long start, unsigned long end)
+{
+	invalidate_mm(mm);
+}
+
+
+#else
+
 #define invalidate() \
 	smp_invalidate()
 
@@ -100,7 +153,7 @@
 {
 	invalidate();
 }
-
+#endif
 #endif
 
 
diff -u --recursive --new-file v1.3.59/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.59/linux/include/asm-i386/smp.h	Wed Dec 27 09:12:14 1995
+++ linux/include/asm-i386/smp.h	Wed Feb  7 08:55:40 1996
@@ -180,7 +180,6 @@
 extern unsigned char boot_cpu_id;
 extern unsigned long cpu_present_map;
 extern volatile unsigned long smp_invalidate_needed;
-extern volatile unsigned long smp_spins;
 extern void smp_invalidate(void);
 extern volatile unsigned long kernel_flag, kernel_counter;
 extern volatile unsigned char active_kernel_processor;
diff -u --recursive --new-file v1.3.59/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h
--- v1.3.59/linux/include/asm-i386/smp_lock.h	Sun Jan 14 16:30:14 1996
+++ linux/include/asm-i386/smp_lock.h	Wed Feb  7 08:55:40 1996
@@ -24,7 +24,7 @@
 			break;
 		do 
 		{
-			smp_spins++;
+			smp_spins[smp_processor_id()]++;
 			/*
 			 *	Doing test_bit here doesn't lock the bus 
 			 */
diff -u --recursive --new-file v1.3.59/linux/include/linux/fdreg.h linux/include/linux/fdreg.h
--- v1.3.59/linux/include/linux/fdreg.h	Sat Jan  6 19:10:40 1996
+++ linux/include/linux/fdreg.h	Wed Feb  7 08:55:40 1996
@@ -110,6 +110,8 @@
 #define FDC_82077_UNKN	0x53	/* Unknown 82077 variant */
 #define FDC_82078	0x60	/* 44pin 82078 or 64pin 82078SL */
 #define FDC_82078_1	0x61	/* 82078-1 (2Mbps fdc) */
+#define FDC_S82078B	0x62	/* S82078B (first seen on Adaptec AVA-2825 VLB
+				 * SCSI/EIDE/Floppy controller) */
 #define FDC_87306	0x63	/* National Semiconductor PC 87306 */
 
 /*
diff -u --recursive --new-file v1.3.59/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.59/linux/include/linux/fs.h	Fri Jan 26 01:37:07 1996
+++ linux/include/linux/fs.h	Wed Feb  7 11:15:20 1996
@@ -236,6 +236,7 @@
 #define ATTR_CTIME	64
 #define ATTR_ATIME_SET	128
 #define ATTR_MTIME_SET	256
+#define ATTR_FORCE	512	/* Not a change, but a change it */
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
@@ -627,6 +628,10 @@
 {
 	return __iget(sb, nr, 1);
 }
+
+/* kludge to get SCSI modules working */
+#include <linux/minix_fs.h>
+#include <linux/minix_fs_sb.h>
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.3.59/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.3.59/linux/include/linux/if_arp.h	Fri Nov 17 08:42:28 1995
+++ linux/include/linux/if_arp.h	Wed Feb  7 08:55:40 1996
@@ -43,6 +43,7 @@
 #define ARPHRD_TUNNEL6	769		/* IPIP6 tunnel			*/
 #define ARPHRD_FRAD	770		/* Frame Relay			*/
 #define ARPHRD_SKIP	771		/* SKIP vif			*/
+#define ARPHRD_LOOPBACK	772		/* Loopback device		*/
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
diff -u --recursive --new-file v1.3.59/linux/include/linux/if_ether.h linux/include/linux/if_ether.h
--- v1.3.59/linux/include/linux/if_ether.h	Mon Aug 28 14:52:23 1995
+++ linux/include/linux/if_ether.h	Wed Feb  7 08:55:40 1996
@@ -56,6 +56,8 @@
 #define ETH_P_802_2	0x0004		/* 802.2 frames 		*/
 #define ETH_P_SNAP	0x0005		/* Internal only		*/
 #define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
+#define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
 
 /* This is an Ethernet frame header. */
 struct ethhdr {
diff -u --recursive --new-file v1.3.59/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.3.59/linux/include/linux/ip_fw.h	Fri Nov 17 08:42:28 1995
+++ linux/include/linux/ip_fw.h	Wed Feb  7 14:04:11 1996
@@ -105,7 +105,9 @@
 #define IP_FW_F_ICMPRPL 0x100	/* Send back icmp unreachable packet  */
 #define IP_FW_F_MASQ	0x200	/* Masquerading			      */
 #define IP_FW_F_TCPACK	0x400	/* For tcp-packets match if ACK is set*/
-#define IP_FW_F_MASK	0x7FF	/* All possible flag bits mask        */
+#define IP_FW_F_APPEND	0x800	/* Dont try to guess placement        */
+
+#define IP_FW_F_MASK	0xFFF	/* All possible flag bits mask        */
 
 /*    
  *	New IP firewall options for [gs]etsockopt at the RAW IP level.
diff -u --recursive --new-file v1.3.59/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v1.3.59/linux/include/linux/msdos_fs.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/msdos_fs.h	Wed Feb  7 11:20:43 1996
@@ -34,6 +34,11 @@
 #define ATTR_NONE    0 /* no attribute bits */
 #define ATTR_UNUSED  (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN)
 	/* attribute bits that are copied "as is" */
+#define ATTR_EXT     (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME)
+	/* bits that are used by the Windows 95/Windows NT extended FAT */
+
+#define CASE_LOWER_BASE 8	/* base is lower case */
+#define CASE_LOWER_EXT  16	/* extension is lower case */
 
 #define SCAN_ANY     0  /* either hidden or not */
 #define SCAN_HID     1  /* only hidden */
@@ -51,6 +56,8 @@
 #define MSDOS_I(i) (&((i)->u.msdos_i))
 
 #define MSDOS_NAME 11 /* maximum name length */
+#define MSDOS_LONGNAME 256 /* maximum name length */
+#define MSDOS_SLOTS 21  /* max # of slots needed for short and long names */
 #define MSDOS_DOT    ".          " /* ".", padded to MSDOS_NAME chars */
 #define MSDOS_DOTDOT "..         " /* "..", padded to MSDOS_NAME chars */
 
@@ -90,11 +97,37 @@
 struct msdos_dir_entry {
 	__s8	name[8],ext[3];	/* name and extension */
 	__u8	attr;		/* attribute bits */
-	__u8	unused[10];
+	__u8    lcase;		/* Case for base and extension */
+	__u8	ctime_ms;	/* Creation time, milliseconds */
+	__u16	ctime;		/* Creation time */
+	__u16	cdate;		/* Creation date */
+	__u16	adate;		/* Last access date */
+	__u8    unused[2];
 	__u16	time,date,start;/* time, date and first cluster */
 	__u32	size;		/* file size (in bytes) */
 };
 
+/* Up to 13 characters of the name */
+struct msdos_dir_slot {
+	__u8    id;		/* sequence number for slot */
+	__u8    name0_4[10];	/* first 5 characters in name */
+	__u8    attr;		/* attribute byte */
+	__u8    reserved;	/* always 0 */
+	__u8    alias_checksum;	/* checksum for 8.3 alias */
+	__u8    name5_10[12];	/* 6 more characters in name */
+	__u8    start[2];	/* starting cluster number */
+	__u8    name11_12[4];	/* last 2 characters in name */
+};
+
+struct slot_info {
+	int is_long;		       /* was the found entry long */
+	int long_slots;		       /* number of long slots in filename */
+	int total_slots;	       /* total slots (long and short) */
+	loff_t longname_offset;	       /* dir offset for longname start */
+	loff_t shortname_offset;       /* dir offset for shortname start */
+	int ino;		       /* ino for the file */
+};
+
 struct fat_cache {
 	kdev_t device; /* device number. 0 means unused. */
 	int ino; /* inode number. */
@@ -113,81 +146,91 @@
 /* Convert the UNIX mode to MS-DOS attribute bits. */
 #define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
 
+
 #ifdef __KERNEL__
 
 /* misc.c */
-
-extern void fs_panic(struct super_block *s,const char *msg);
 extern int is_binary(char conversion,char *extension);
-extern void lock_creation(void);
-extern void unlock_creation(void);
 extern void lock_fat(struct super_block *sb);
 extern void unlock_fat(struct super_block *sb);
-extern int msdos_add_cluster(struct inode *inode);
+extern int fat_add_cluster(struct inode *inode);
 extern int date_dos2unix(__u16 time, __u16 date);
-extern void date_unix2dos(int unix_date,__u16 *time, __u16 *date);
-extern int msdos_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
-    struct msdos_dir_entry **de);
-extern int msdos_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de,int *ino,char scantype);
-extern int msdos_parent_ino(struct inode *dir,int locked);
-extern int msdos_subdirs(struct inode *dir);
+extern void fat_fs_panic(struct super_block *s,const char *msg);
+extern void fat_lock_creation(void);
+extern void fat_unlock_creation(void);
+extern void fat_date_unix2dos(int unix_date,__u16 *time, __u16 *date);
+extern int fat_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
+			 struct msdos_dir_entry **de);
+extern int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
+		    struct msdos_dir_entry **res_de,int *ino,char scantype);
+extern int fat_parent_ino(struct inode *dir,int locked);
+extern int fat_subdirs(struct inode *dir);
 
 /* fat.c */
-
 extern int fat_access(struct super_block *sb,int nr,int new_value);
-extern int msdos_smap(struct inode *inode,int sector);
+extern int fat_smap(struct inode *inode,int sector);
 extern int fat_free(struct inode *inode,int skip);
+void fat_cache_inval_inode(struct inode *inode);
+void fat_cache_inval_dev(kdev_t device);
 extern void cache_init(void);
 void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu);
 void cache_add(struct inode *inode,int f_clu,int d_clu);
-void cache_inval_inode(struct inode *inode);
-void cache_inval_dev(kdev_t device);
 int get_cluster(struct inode *inode,int cluster);
 
-/* namei.c */
-
-extern int msdos_lookup(struct inode *dir,const char *name,int len,
-                        struct inode **result);
-extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
-	struct inode **result);
-extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
-extern int msdos_rmdir(struct inode *dir,const char *name,int len);
-extern int msdos_unlink(struct inode *dir,const char *name,int len);
-extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
-extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
-	struct inode *new_dir,const char *new_name,int new_len);
-
 /* inode.c */
-
-extern void msdos_put_inode(struct inode *inode);
-extern void msdos_put_super(struct super_block *sb);
-extern struct super_block *msdos_read_super(struct super_block *s,
-					    void *data,int);
-extern int init_msdos_fs(void);
-extern void msdos_statfs(struct super_block *sb,struct statfs *buf, int);
-extern int msdos_bmap(struct inode *inode,int block);
-extern void msdos_read_inode(struct inode *inode);
-extern void msdos_write_inode(struct inode *inode);
-extern int msdos_notify_change(struct inode *,struct iattr *);
+extern int fat_bmap(struct inode *inode,int block);
+extern int fat_notify_change(struct inode *,struct iattr *);
+extern void fat_put_inode(struct inode *inode);
+extern void fat_put_super(struct super_block *sb);
+extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
+extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
+extern void fat_statfs(struct super_block *sb,struct statfs *buf, int);
+extern void fat_write_inode(struct inode *inode);
 
 /* dir.c */
+extern struct file_operations fat_dir_operations;
+extern int fat_readdir(struct inode *inode, struct file *filp,
+		       void *dirent, filldir_t);
+extern int fat_dir_ioctl(struct inode * inode, struct file * filp,
+			 unsigned int cmd, unsigned long arg);
 
-extern struct inode_operations msdos_dir_inode_operations;
-extern int msdos_readdir (struct inode *inode, struct file *filp,
-	void *dirent, filldir_t);
 /* file.c */
+extern struct inode_operations fat_file_inode_operations;
+extern struct inode_operations fat_file_inode_operations_1024;
+extern int fat_file_read(struct inode *, struct file *, char *, int);
+extern int fat_file_write(struct inode *, struct file *, const char *, int);
+extern void fat_truncate(struct inode *inode);
 
-extern struct inode_operations msdos_file_inode_operations;
-extern struct inode_operations msdos_file_inode_operations_1024;
-extern int msdos_file_read(struct inode *, struct file *, char *, int);
-extern int msdos_file_write(struct inode *, struct file *, const char *, int);
-extern struct inode_operations msdos_file_inode_operations_no_bmap;
+/* mmap.c */
+extern int fat_mmap(struct inode *, struct file *, struct vm_area_struct *);
 
-extern void msdos_truncate(struct inode *inode);
 
-/* mmap.c */
-extern int msdos_mmap(struct inode *, struct file *, struct vm_area_struct *);
+/* vfat.c */
+extern int init_vfat_fs(void);
+
+
+/* msdosfs_syms.c */
+extern int init_msdos_fs(void);
+extern struct file_system_type msdos_fs_type;
+
+/* msdos.c */
+extern struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent);
+
+/* msdos.c - these are for Umsdos */
+extern void msdos_read_inode(struct inode *inode);
+extern int msdos_lookup(struct inode *dir,const char *name,int len, 
+			struct inode **result);
+extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
+			struct inode **result);
+extern int msdos_rmdir(struct inode *dir,const char *name,int len);
+extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
+extern int msdos_unlink(struct inode *dir,const char *name,int len);
+extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
+extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
+			struct inode *new_dir,const char *new_name,int new_len);
+
+/* fatfs_syms.c */
+extern int init_fat_fs(void);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.3.59/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h
--- v1.3.59/linux/include/linux/msdos_fs_i.h	Thu Jan  5 13:55:40 1995
+++ linux/include/linux/msdos_fs_i.h	Wed Feb  7 09:39:29 1996
@@ -34,6 +34,7 @@
 	struct inode *i_old;	/* pointer to the old inode this inode
 				   depends on */
 	int i_binary;	/* file contains non-text data */
+	int sys_immutable;	/* file is an immutable system file */
 };
 
 #endif
diff -u --recursive --new-file v1.3.59/linux/include/linux/msdos_fs_sb.h linux/include/linux/msdos_fs_sb.h
--- v1.3.59/linux/include/linux/msdos_fs_sb.h	Sat Nov 11 17:41:34 1995
+++ linux/include/linux/msdos_fs_sb.h	Wed Feb  7 09:39:29 1996
@@ -23,6 +23,9 @@
 	int prev_free; /* previously returned free cluster number */
 	int free_clusters; /* -1 if undefined */
 	char dotsOK;
+	char sys_immutable; /* system files are immutable */
+	int vfat; /* 0=no vfat long filename support, 1=vfat support */
+	int umsdos; /* 1 if mounted by umsdos, 0 if not */
 };
 
 #endif
diff -u --recursive --new-file v1.3.59/linux/include/linux/ncp_fs_i.h linux/include/linux/ncp_fs_i.h
--- v1.3.59/linux/include/linux/ncp_fs_i.h	Tue Jan  2 16:46:30 1996
+++ linux/include/linux/ncp_fs_i.h	Wed Feb  7 08:55:41 1996
@@ -1,7 +1,7 @@
 /*
  *  ncp_fs_i.h
  *
- *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1995 Volker Lendecke
  *
  */
 
diff -u --recursive --new-file v1.3.59/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.3.59/linux/include/linux/netdevice.h	Wed Dec 13 09:02:47 1995
+++ linux/include/linux/netdevice.h	Wed Feb  7 13:55:33 1996
@@ -49,7 +49,7 @@
 #define IS_MULTICAST	5		/* Multicast IP address */
 
 /*
- *	We tag these structures with multicasts.
+ *	We tag multicasts with these structures.
  */
  
 struct dev_mc_list
@@ -147,6 +147,7 @@
   int			 mc_count;	/* Number of installed mcasts	*/
   
   struct ip_mc_list	 *ip_mc_list;	/* IP multicast filter chain    */
+  __u32			tx_queue_len;	/* Max frames per queue allowed */
     
   /* For load balancing driver pair support */
   
@@ -155,7 +156,6 @@
   struct net_alias_info		*alias_info;	/* main dev alias info */
   struct net_alias		*my_alias;	/* alias devs */
   
-
   /* Pointer to the interface buffers. */
   struct sk_buff_head	  buffs[DEV_NUMBUFFS];
 
@@ -227,9 +227,6 @@
 				       int pri);
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
-/* The old interface to netif_rx(). */
-extern int		dev_rint(unsigned char *buff, long len, int flags,
-				 struct device * dev);
 extern void		dev_transmit(void);
 extern int		in_net_bh(void);
 extern void		net_bh(void *tmp);
diff -u --recursive --new-file v1.3.59/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v1.3.59/linux/include/linux/pagemap.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/pagemap.h	Wed Feb  7 08:55:41 1996
@@ -1,6 +1,8 @@
 #ifndef _LINUX_PAGEMAP_H
 #define _LINUX_PAGEMAP_H
 
+#include <asm/system.h>
+
 /*
  * Page-mapping primitive inline functions
  *
@@ -42,14 +44,18 @@
 static inline struct page * find_page(struct inode * inode, unsigned long offset)
 {
 	struct page *page;
-
+	unsigned long flags;
+	
 	for (page = page_hash(inode, offset); page ; page = page->next_hash) {
 		if (page->inode != inode)
 			continue;
 		if (page->offset != offset)
 			continue;
+		save_flags(flags);
+		cli();
 		page->referenced = 1;
 		page->count++;
+		restore_flags(flags);
 		break;
 	}
 	return page;
diff -u --recursive --new-file v1.3.59/linux/include/linux/pci.h linux/include/linux/pci.h
--- v1.3.59/linux/include/linux/pci.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/pci.h	Thu Jan 25 21:01:32 1996
@@ -263,6 +263,7 @@
 #define PCI_DEVICE_ID_CIRRUS_5434_4	0x00a4
 #define PCI_DEVICE_ID_CIRRUS_5434_8	0x00a8
 #define PCI_DEVICE_ID_CIRRUS_5436	0x00ac
+#define PCI_DEVICE_ID_CIRRUS_6205	0x0205
 #define PCI_DEVICE_ID_CIRRUS_6729	0x1100
 #define PCI_DEVICE_ID_CIRRUS_7542	0x1200
 #define PCI_DEVICE_ID_CIRRUS_7543	0x1202
@@ -320,6 +321,8 @@
 #define PCI_DEVICE_ID_OPTI_82C621	0xc621
 #define PCI_DEVICE_ID_OPTI_82C822	0xc822
 
+#define PCI_VENDOR_ID_SGS		0x104a
+
 #define PCI_VENDOR_ID_BUSLOGIC		0x104B
 #define PCI_DEVICE_ID_BUSLOGIC_946C_2	0x0140
 #define PCI_DEVICE_ID_BUSLOGIC_946C	0x1040
@@ -330,6 +333,7 @@
 
 #define PCI_VENDOR_ID_N9		0x105D
 #define PCI_DEVICE_ID_N9_I128		0x2309
+#define PCI_DEVICE_ID_N9_I128_2		0x2339
 
 #define PCI_VENDOR_ID_UMC		0x1060
 #define PCI_DEVICE_ID_UMC_UM8673F	0x0101
@@ -345,6 +349,8 @@
 #define PCI_VENDOR_ID_X			0x1061
 #define PCI_DEVICE_ID_X_AGX016		0x0001
 
+#define PCI_VENDOR_ID_NEXGEN		0x1074
+
 #define PCI_VENDOR_ID_QLOGIC		0x1077
 #define PCI_DEVICE_ID_QLOGIC_ISP1020	0x1020
 #define PCI_DEVICE_ID_QLOGIC_ISP1022	0x1022
@@ -400,13 +406,20 @@
 
 #define PCI_VENDOR_ID_AMCC		0x10e8
 
+#define PCI_VENDOR_ID_INTERG		0x10ea
+#define PCI_DEVICE_ID_INTERG_1680	0x1680
+
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8029	0x8029
 
+#define PCI_VENDOR_ID_INIT		0x1101
+#define PCI_DEVICE_ID_INIT_320P		0x9100
+
 #define PCI_VENDOR_ID_VIA		0x1106
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
 #define PCI_DEVICE_ID_VIA_82C576	0x0576
+#define PCI_DEVICE_ID_VIA_82C416	0x1571
 
 #define PCI_VENDOR_ID_VORTEX		0x1119
 #define PCI_DEVICE_ID_VORTEX_GDT	0x0001
@@ -414,6 +427,9 @@
 #define PCI_VENDOR_ID_EF		0x111a
 #define PCI_DEVICE_ID_EF_ATM_FPGA	0x0000
 #define PCI_DEVICE_ID_EF_ATM_ASIC	0x0002
+
+#define PCI_VENDOR_ID_FORE		0x1127
+#define PCI_DEVICE_ID_FORE_PCA200PC	0x0210
 
 #define PCI_VENDOR_ID_IMAGINGTECH	0x112f
 #define PCI_DEVICE_ID_IMAGINGTECH_ICPCI	0x0000
diff -u --recursive --new-file v1.3.59/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.59/linux/include/linux/proc_fs.h	Fri Jan 26 01:37:07 1996
+++ linux/include/linux/proc_fs.h	Wed Feb  7 11:15:36 1996
@@ -35,6 +35,9 @@
 	PROC_DMA,	
 	PROC_IOPORTS,
 	PROC_APM,
+#ifdef __SMP_PROF__
+	PROC_SMP_PROF,
+#endif
 	PROC_PROFILE, /* whether enabled or not */
 	PROC_CMDLINE,
 	PROC_SYS
@@ -163,6 +166,9 @@
 	struct proc_dir_entry *next, *parent, *subdir;
 	void *data;
 };
+
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+				off_t offset, int length, int inout);
 
 extern struct proc_dir_entry proc_root;
 extern struct proc_dir_entry proc_net;
diff -u --recursive --new-file v1.3.59/linux/include/linux/quota.h linux/include/linux/quota.h
--- v1.3.59/linux/include/linux/quota.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/quota.h	Wed Feb  7 08:04:32 1996
@@ -70,6 +70,8 @@
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
 
+#include <linux/mount.h>
+
 /*
  * Definitions for the default names of the quotas files.
  */
diff -u --recursive --new-file v1.3.59/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.3.59/linux/include/linux/sbpcd.h	Sat Jan  6 19:10:40 1996
+++ linux/include/linux/sbpcd.h	Wed Feb  7 08:17:50 1996
@@ -5,22 +5,28 @@
 /*
  * Attention! This file contains user-serviceable parts!
  * I recommend to make use of it...
+ * If you feel helpless, look into linux/Documentation/cdrom/sbpcd
+ * (good idea anyway, at least before mailing me).
  *
  * The definitions for the first controller can get overridden by
  * the kernel command line ("lilo boot option").
  * Examples:
- *                                 sbpcd=0x230,SoundBlaster
- *                             or
  *                                 sbpcd=0x300,LaserMate
  *                             or
+ *                                 sbpcd=0x230,SoundBlaster
+ *                             or
  *                                 sbpcd=0x338,SoundScape
+ *                             or
+ *                                 sbpcd=0x2C0,Teac16bit
  *
  * If sbpcd gets used as a module, you can load it with
- *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
+ *     insmod sbpcd.o sbpcd=0x300,0
  * or
- *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x300,0
+ *     insmod sbpcd.o sbpcd=0x230,1
  * or
- *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x338,2
+ *     insmod sbpcd.o sbpcd=0x338,2
+ * or
+ *     insmod sbpcd.o sbpcd=0x2C0,3
  * respective to override the configured address and type.
  */
 
@@ -28,9 +34,6 @@
  * define your CDROM port base address as CDROM_PORT
  * and specify the type of your interface card as SBPRO.
  *
- * Read linux/drivers/block/README.sbpcd if you are in doubt about the
- * type of your interface card (you should do that anyway).
- *
  * address:
  * ========
  * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
@@ -54,14 +57,13 @@
  * set SBPRO to 0 for "compatible" soundcards and
  *                for "poor" (no sound) interface cards.
  * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
+ * set SBPRO to 3 for Teac 16bit interface cards
  *
  * Almost all "compatible" sound boards need to set SBPRO to 0.
  * If SBPRO is set wrong, the drives will get found - but any
  * data access will give errors (audio access will work).
- * The "OmniCD" no-sound interface card from CreativeLabs needs SBPRO 1.
- *
- * mail to emoenke@gwdg.de if you have a "compatible" sound card which
- * in fact needs to set SBPRO to 1 (not any known at time).
+ * The "OmniCD" no-sound interface card from CreativeLabs and most Teac
+ * interface cards need SBPRO 1.
  *
  * sound base:
  * ===========
@@ -85,7 +87,7 @@
 #define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0   */
 #endif
 #if !(SBPCD_ISSUE-3)     /* ===================== third interface board: === */
-#define CDROM_PORT 0x634 /* <-----------<< port address                      */
+#define CDROM_PORT 0x630 /* <-----------<< port address                      */
 #define SBPRO      1     /* <-----------<< interface type                    */
 #define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
 #define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0   */
@@ -101,7 +103,7 @@
  * some more or less user dependent definitions - service them!
  */
 
-/* Set this to 0 after you have configured your interface definitions right. */
+/* Set this to 0 once you have configured your interface definitions right. */
 #define DISTRIBUTION 1
 
 #if DISTRIBUTION
@@ -137,6 +139,9 @@
 #define SPEA_TEST 0
 #define TEST_STI 0
 #undef PATH_CHECK
+#ifndef SOUND_BASE
+#define SOUND_BASE 0
+#endif
 /*==========================================================================*/
 /*
  * DDI interface definitions
@@ -427,6 +432,8 @@
 
 /*==========================================================================*/
 
+#define MIXER_addr SOUND_BASE+4 /* sound card's address register */
+#define MIXER_data SOUND_BASE+5 /* sound card's data register */
 #define MIXER_CD_Volume	0x28	/* internal SB Pro register address */
 
 /*==========================================================================*/
diff -u --recursive --new-file v1.3.59/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.3.59/linux/include/linux/sched.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/sched.h	Wed Feb  7 11:15:21 1996
@@ -22,6 +22,8 @@
 #include <asm/page.h>
 
 #include <linux/smp.h>
+#include <linux/tty.h>
+#include <linux/sem.h>
 
 /*
  * cloning flags:
diff -u --recursive --new-file v1.3.59/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.59/linux/include/linux/skbuff.h	Sat Nov 25 19:04:56 1995
+++ linux/include/linux/skbuff.h	Wed Feb  7 13:55:33 1996
@@ -28,73 +28,81 @@
 #define FREE_WRITE	0
 
 
-struct sk_buff_head {
-  struct sk_buff		* volatile next;
-  struct sk_buff		* volatile prev;
+struct sk_buff_head 
+{
+	struct sk_buff	* volatile next;
+	struct sk_buff	* volatile prev;
+	__u32		qlen;		/* Must be same length as a pointer
+					   for using debugging */
 #if CONFIG_SKB_CHECK
-  int				magic_debug_cookie;
+	int		magic_debug_cookie;
 #endif
 };
 
 
-struct sk_buff {
-  struct sk_buff		* volatile next;	/* Next buffer in list */
-  struct sk_buff		* volatile prev;	/* Previous buffer in list */
+struct sk_buff 
+{
+	struct sk_buff	* volatile next;	/* Next buffer in list 				*/
+	struct sk_buff	* volatile prev;	/* Previous buffer in list 			*/
+	struct sk_buff_head * list;		/* List we are on				*/
 #if CONFIG_SKB_CHECK
-  int				magic_debug_cookie;
+	int		magic_debug_cookie;
 #endif
-  struct sk_buff		* volatile link3;	/* Link for IP protocol level buffer chains 	*/
-  struct sock			*sk;			/* Socket we are owned by 			*/
-  volatile unsigned long	when;			/* used to compute rtt's			*/
-  struct timeval		stamp;			/* Time we arrived				*/
-  struct device			*dev;			/* Device we arrived on/are leaving by		*/
-  union {
-	struct tcphdr	*th;
-	struct ethhdr	*eth;
-	struct iphdr	*iph;
-	struct udphdr	*uh;
-	unsigned char	*raw;
-  } h;
+	struct sk_buff	* volatile link3;	/* Link for IP protocol level buffer chains 	*/
+	struct sock	*sk;			/* Socket we are owned by 			*/
+	unsigned long	when;			/* used to compute rtt's			*/
+	struct timeval	stamp;			/* Time we arrived				*/
+	struct device	*dev;			/* Device we arrived on/are leaving by		*/
+	union 
+	{
+		struct tcphdr	*th;
+		struct ethhdr	*eth;
+		struct iphdr	*iph;
+		struct udphdr	*uh;
+		unsigned char	*raw;
+	} h;
   
-  union {						/* As yet incomplete physical layer views 	*/
-  	unsigned char 		*raw;
-  	struct ethhdr		*ethernet;
-  } mac;
+	union 
+	{	
+		/* As yet incomplete physical layer views */
+	  	unsigned char 	*raw;
+	  	struct ethhdr	*ethernet;
+	} mac;
   
-  struct iphdr			*ip_hdr;		/* For IPPROTO_RAW 				*/
-  unsigned long 		len;			/* Length of actual data			*/
-  unsigned long			csum;			/* Checksum 					*/
-  __u32				saddr;			/* IP source address				*/
-  __u32				daddr;			/* IP target address				*/
-  __u32				raddr;			/* IP next hop address				*/
-  __u32				seq;			/* TCP sequence number				*/
-  __u32				end_seq;		/* seq [+ fin] [+ syn] + datalen		*/
-  __u32				ack_seq;		/* TCP ack sequence number			*/
-  unsigned char			proto_priv[16];	        /* Protocol private data			*/
-  volatile char 		acked,			/* Are we acked ?				*/
-				used,			/* Are we in use ?				*/
-				free,			/* How to free this buffer			*/
-				arp;			/* Has IP/ARP resolution finished		*/
-  unsigned char			tries,			/* Times tried					*/
-  				lock,			/* Are we locked ?				*/
-  				localroute,		/* Local routing asserted for this frame	*/
-  				pkt_type,		/* Packet class					*/
-  				ip_summed;		/* Driver fed us an IP checksum			*/
-#define PACKET_HOST		0			/* To us					*/
-#define PACKET_BROADCAST	1			/* To all					*/
-#define PACKET_MULTICAST	2			/* To group					*/
-#define PACKET_OTHERHOST	3			/* To someone else 				*/
-  unsigned short		users;			/* User count - see datagram.c,tcp.c 		*/
-  unsigned short		protocol;		/* Packet protocol from driver. 		*/
-  unsigned short		truesize;		/* Buffer size 					*/
-
-  int				count;			/* reference count				*/
-  struct sk_buff		*data_skb;		/* Link to the actual data skb			*/
-  unsigned char			*head;			/* Head of buffer 				*/
-  unsigned char			*data;			/* Data head pointer				*/
-  unsigned char			*tail;			/* Tail pointer					*/
-  unsigned char 		*end;			/* End pointer					*/
-  void 				(*destructor)(struct sk_buff *this);	/* Destruct function		*/
+	struct iphdr	*ip_hdr;		/* For IPPROTO_RAW 				*/
+	unsigned long 	len;			/* Length of actual data			*/
+	unsigned long	csum;			/* Checksum 					*/
+	__u32		saddr;			/* IP source address				*/
+	__u32		daddr;			/* IP target address				*/
+	__u32		raddr;			/* IP next hop address				*/
+	__u32		seq;			/* TCP sequence number				*/
+	__u32		end_seq;		/* seq [+ fin] [+ syn] + datalen		*/
+	__u32		ack_seq;		/* TCP ack sequence number			*/
+	unsigned char	proto_priv[16];	        /* Protocol private data			*/
+	volatile char 	acked,			/* Are we acked ?				*/
+			used,			/* Are we in use ?				*/
+			free,			/* How to free this buffer			*/
+			arp;			/* Has IP/ARP resolution finished		*/
+	unsigned char	tries,			/* Times tried					*/
+  			lock,			/* Are we locked ?				*/
+  			localroute,		/* Local routing asserted for this frame	*/
+  			pkt_type,		/* Packet class					*/
+  			ip_summed;		/* Driver fed us an IP checksum			*/
+#define PACKET_HOST		0		/* To us					*/
+#define PACKET_BROADCAST	1		/* To all					*/
+#define PACKET_MULTICAST	2		/* To group					*/
+#define PACKET_OTHERHOST	3		/* To someone else 				*/
+	unsigned short	users;			/* User count - see datagram.c,tcp.c 		*/
+	unsigned short	protocol;		/* Packet protocol from driver. 		*/
+	unsigned short	truesize;		/* Buffer size 					*/
+
+	int		count;			/* reference count				*/
+	struct sk_buff	*data_skb;		/* Link to the actual data skb			*/
+	unsigned char	*head;			/* Head of buffer 				*/
+	unsigned char	*data;			/* Data head pointer				*/
+	unsigned char	*tail;			/* Tail pointer					*/
+	unsigned char 	*end;			/* End pointer					*/
+	void 		(*destructor)(struct sk_buff *this);	/* Destruct function		*/
 };
 
 #ifdef CONFIG_SKB_LARGE
@@ -129,11 +137,13 @@
 extern void 			skb_insert(struct sk_buff *old,struct sk_buff *newsk);
 extern void			skb_append(struct sk_buff *old,struct sk_buff *newsk);
 extern void			skb_unlink(struct sk_buff *buf);
+extern __u32			skb_queue_len(struct sk_buff_head *list);
 extern struct sk_buff *		skb_peek_copy(struct sk_buff_head *list);
 extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
 extern struct sk_buff *		dev_alloc_skb(unsigned int size);
 extern void			kfree_skbmem(struct sk_buff *skb);
 extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
+extern struct sk_buff *		skb_copy(struct sk_buff *skb, int priority);
 extern void			skb_device_lock(struct sk_buff *skb);
 extern void			skb_device_unlock(struct sk_buff *skb);
 extern void			dev_kfree_skb(struct sk_buff *skb, int mode);
@@ -152,12 +162,21 @@
  *	list and someone else may run off with it. For an interrupt
  *	type system cli() peek the buffer copy the data and sti();
  */
-static __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+extern __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
 {
 	struct sk_buff *list = (struct sk_buff *)list_;
 	return (list->next != list)? list->next : NULL;
 }
 
+/*
+ *	Return the length of an sk_buff queue
+ */
+ 
+extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
+{
+	return(list_->qlen);
+}
+
 #if CONFIG_SKB_CHECK
 extern int 			skb_check(struct sk_buff *skb,int,int, char *);
 #define IS_SKB(skb)		skb_check((skb), 0, __LINE__,__FILE__)
@@ -170,6 +189,7 @@
 {
 	list->prev = (struct sk_buff *)list;
 	list->next = (struct sk_buff *)list;
+	list->qlen = 0;
 }
 
 /*
@@ -187,6 +207,8 @@
 	newsk->prev = list;
 	newsk->next->prev = newsk;
 	newsk->prev->next = newsk;
+	newsk->list = list_;
+	list_->qlen++;
 	restore_flags(flags);
 }
 
@@ -207,7 +229,8 @@
 
 	newsk->next->prev = newsk;
 	newsk->prev->next = newsk;
-
+	newsk->list=list_;
+	list_->qlen++;
 	restore_flags(flags);
 }
 
@@ -241,6 +264,8 @@
 		result->next = NULL;
 		result->prev = NULL;
 
+		list_->qlen--;
+		result->list=NULL;
 		restore_flags(flags);
 
 		return result;
@@ -261,7 +286,8 @@
 	newsk->prev = old->prev;
 	old->prev = newsk;
 	newsk->prev->next = newsk;
-
+	newsk->list = old->list;
+	newsk->list->qlen++;
 	restore_flags(flags);
 }
 
@@ -280,6 +306,8 @@
 	newsk->next = old->next;
 	newsk->next->prev = newsk;
 	old->next = newsk;
+	newsk->list = old->list;
+	newsk->list->qlen++;
 
 	restore_flags(flags);
 }
@@ -298,12 +326,14 @@
 	save_flags(flags);
 	cli();
 
-	if(skb->prev && skb->next)
+	if(skb->list)
 	{
+		skb->list->qlen--;
 		skb->next->prev = skb->prev;
 		skb->prev->next = skb->next;
 		skb->next = NULL;
 		skb->prev = NULL;
+		skb->list = NULL;
 	}
 	restore_flags(flags);
 }
diff -u --recursive --new-file v1.3.59/linux/include/linux/smp.h linux/include/linux/smp.h
--- v1.3.59/linux/include/linux/smp.h	Sat Jan  6 19:10:40 1996
+++ linux/include/linux/smp.h	Wed Feb  7 08:55:41 1996
@@ -15,7 +15,18 @@
 extern void smp_commence(void);			/* Multiprocessors may now schedule */
 extern int smp_num_cpus;
 extern int smp_threads_ready;			/* True once the per process idle is forked */
-extern volatile unsigned long smp_spins;	/* Count of spins */
+#ifdef __SMP_PROF__
+extern volatile unsigned long smp_spins[NR_CPUS];	/* count of interrupt spins */
+extern volatile unsigned long smp_spins_sys_idle[];	/* count of idle spins */
+extern volatile unsigned long smp_spins_syscall[];	/* count of syscall spins */
+extern volatile unsigned long smp_spins_syscall_cur[];	/* count of syscall spins for the current
+							   call */
+extern volatile unsigned long smp_idle_count[1+NR_CPUS];/* count idle ticks */
+extern volatile unsigned long smp_idle_map;		/* map with idle cpus */
+#else
+exern volatile unsigned long smp_spins;
+#endif
+
 
 extern volatile unsigned long smp_msg_data;
 extern volatile int smp_src_cpu;
diff -u --recursive --new-file v1.3.59/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v1.3.59/linux/include/linux/swapctl.h	Tue Jan 23 21:15:54 1996
+++ linux/include/linux/swapctl.h	Wed Feb  7 11:16:18 1996
@@ -53,6 +53,8 @@
 typedef swapstat_v1 swapstat_t;
 extern swapstat_t swapstats;
 
+extern int min_free_pages, free_pages_low, free_pages_high;
+
 #define SC_VERSION	1
 #define SC_MAX_VERSION	1
 
diff -u --recursive --new-file v1.3.59/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v1.3.59/linux/include/linux/sysctl.h	Tue Jan 23 21:15:54 1996
+++ linux/include/linux/sysctl.h	Wed Feb  7 08:55:41 1996
@@ -59,7 +59,8 @@
 /* CTL_VM names: */
 #define VM_SWAPCTL	1	/* struct: Set vm swapping control */
 #define VM_KSWAPD	2	/* struct: control background pagout */
-#define VM_MAXID	3
+#define VM_FREEPG	3	/* struct: Set free page thresholds */
+#define VM_MAXID	4
 
 /* CTL_NET names: */
 
diff -u --recursive --new-file v1.3.59/linux/include/net/ax25.h linux/include/net/ax25.h
--- v1.3.59/linux/include/net/ax25.h	Mon Dec 11 15:42:06 1995
+++ linux/include/net/ax25.h	Wed Feb  7 13:55:33 1996
@@ -38,6 +38,7 @@
 
 #define SSSID_SPARE	0x60	/* Unused bits in SSID for standard AX.25 */
 #define ESSID_SPARE	0x20	/* Unused bits in SSID for extended AX.25 */
+#define DAMA_FLAG	0x40	/* Well, it is *NOT* unused! (dl1bke 951121 */
 
 #define AX25_REPEATED	0x80
 
@@ -142,6 +143,7 @@
 	struct ax25_cb		*next;
 	ax25_address		source_addr, dest_addr;
 	struct device		*device;
+	unsigned char		dama_slave;	/* dl1bke 951121 */
 	unsigned char		state, modulus, hdrincl;
 	unsigned short		vs, vr, va;
 	unsigned char		condition, backoff;
@@ -160,6 +162,7 @@
 } ax25_cb;
 
 /* af_ax25.c */
+extern ax25_address null_ax25_address;
 extern char *ax2asc(ax25_address *);
 extern int  ax25cmp(ax25_address *, ax25_address *);
 extern int  ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *, struct device *);
@@ -172,11 +175,12 @@
 extern int  ax25_uid_policy;
 extern ax25_address *ax25_findbyuid(uid_t);
 extern void ax25_queue_xmit(struct sk_buff *, struct device *, int);
+extern int  ax25_dev_is_dama_slave(struct device *);	/* dl1bke 951121 */
 
 #include <net/ax25call.h>
 
 /* ax25_in.c */
-extern int  ax25_process_rx_frame(ax25_cb *, struct sk_buff *, int);
+extern int  ax25_process_rx_frame(ax25_cb *, struct sk_buff *, int, int);
 
 /* ax25_out.c */
 extern void ax25_output(ax25_cb *, struct sk_buff *);
@@ -189,12 +193,16 @@
 extern void ax25_timeout_response(ax25_cb *);
 extern void ax25_check_iframes_acked(ax25_cb *, unsigned short);
 extern void ax25_check_need_response(ax25_cb *, int, int);
+extern void dama_enquiry_response(ax25_cb *);			/* dl1bke 960114 */
+extern void dama_check_need_response(ax25_cb *, int, int);	/* dl1bke 960114 */
+extern void dama_establish_data_link(ax25_cb *);
 
 /* ax25_route.c */
 extern void ax25_rt_rx_frame(ax25_address *, struct device *, ax25_digi *);
 extern int  ax25_rt_get_info(char *, char **, off_t, int, int);
 extern int  ax25_cs_get_info(char *, char **, off_t, int, int);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
+extern void ax25_rt_build_path(ax25_cb *, ax25_address *);
 extern void ax25_rt_device_down(struct device *);
 extern int  ax25_rt_ioctl(unsigned int, void *);
 extern void ax25_ip_mode_set(ax25_address *, struct device *, char);
@@ -217,14 +225,21 @@
 extern unsigned short ax25_calculate_t1(ax25_cb *);
 extern void ax25_calculate_rtt(ax25_cb *);
 extern unsigned char *ax25_parse_addr(unsigned char *, int, ax25_address *,
-	ax25_address *, ax25_digi *, int *);
+	ax25_address *, ax25_digi *, int *, int *);	/* dl1bke 951121 */
 extern int  build_ax25_addr(unsigned char *, ax25_address *, ax25_address *,
 	ax25_digi *, int, int);
 extern int  size_ax25_addr(ax25_digi *);
 extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
 extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
+extern void ax25_dama_on(ax25_cb *);	/* dl1bke 951121 */
+extern void ax25_dama_off(ax25_cb *);	/* dl1bke 951121 */
 
 /* ax25_timer */
 extern void ax25_set_timer(ax25_cb *);
+extern void ax25_t1_timeout(ax25_cb *);
+
+/* ... */
+
+extern ax25_cb * volatile ax25_list;
 
 #endif
diff -u --recursive --new-file v1.3.59/linux/include/net/ip.h linux/include/net/ip.h
--- v1.3.59/linux/include/net/ip.h	Fri Nov 17 08:42:29 1995
+++ linux/include/net/ip.h	Wed Feb  7 13:55:33 1996
@@ -46,7 +46,8 @@
 extern void		ip_mc_dropdevice(struct device *dev);
 extern int		ip_mc_procinfo(char *, char **, off_t, int, int);
 #endif
- 
+
+#include <net/ip_forward.h> 
 
 /* Describe an IP fragment. */
 struct ipfrag 
diff -u --recursive --new-file v1.3.59/linux/include/net/ip_forward.h linux/include/net/ip_forward.h
--- v1.3.59/linux/include/net/ip_forward.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/ip_forward.h	Wed Feb  7 08:55:42 1996
@@ -0,0 +1,10 @@
+#ifndef __NET_IP_FORWARD_H
+#define __NET_IP_FORWARD_H
+
+#define IPFWD_FRAGMENT		1
+#define IPFWD_LASTFRAG		2
+#define IPFWD_MASQUERADED	4
+#define IPFWD_MULTICASTING	8
+#define IPFWD_MULTITUNNEL	16
+
+#endif
diff -u --recursive --new-file v1.3.59/linux/include/net/ipx.h linux/include/net/ipx.h
--- v1.3.59/linux/include/net/ipx.h	Tue Aug 15 20:39:05 1995
+++ linux/include/net/ipx.h	Wed Feb  7 13:55:33 1996
@@ -23,6 +23,7 @@
 } ipx_address;
 
 #define ipx_broadcast_node	"\377\377\377\377\377\377"
+#define ipx_this_node           "\0\0\0\0\0\0"
 
 typedef struct ipx_packet
 {
diff -u --recursive --new-file v1.3.59/linux/include/net/slhc.h linux/include/net/slhc.h
--- v1.3.59/linux/include/net/slhc.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/slhc.h	Wed Feb  7 08:55:43 1996
@@ -0,0 +1,6 @@
+#ifndef __NET_SLHC_H
+#define __NET_SLHC_H
+
+extern void slhc_install(void);
+
+#endif
diff -u --recursive --new-file v1.3.59/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.59/linux/include/net/sock.h	Wed Dec 27 09:12:14 1995
+++ linux/include/net/sock.h	Wed Feb  7 13:55:33 1996
@@ -149,6 +149,9 @@
 	__u32			rcv_saddr;	/* Bound address */
 	unsigned short		max_unacked;
 	unsigned short		window;
+	__u32                   lastwin_seq;    /* sequence number when we last updated the window we offer */
+	volatile unsigned long  ato;            /* ack timeout */
+	volatile unsigned long  lrcvtime;       /* jiffies at last rcv */
 	unsigned short		bytes_rcv;
 /*
  *	mss is min(mtu, max_window) 
@@ -195,6 +198,9 @@
 	ipx_address		ipx_dest_addr;
 	ipx_interface		*ipx_intrfc;
 	unsigned short		ipx_port;
+#ifdef CONFIG_IPX_INTERN
+	unsigned char           ipx_node[IPX_NODE_LEN];
+#endif
 	unsigned short		ipx_type;
 #endif
 #ifdef CONFIG_AX25
diff -u --recursive --new-file v1.3.59/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.59/linux/include/net/tcp.h	Wed Sep 13 12:45:33 1995
+++ linux/include/net/tcp.h	Wed Feb  7 08:55:43 1996
@@ -133,7 +133,6 @@
 
 extern int	tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 
-extern int tcp_select_window(struct sock *sk);
 extern void tcp_send_check(struct tcphdr *th, unsigned long saddr, 
 		unsigned long daddr, int len, struct sock *sk);
 extern void tcp_send_probe0(struct sock *sk);
diff -u --recursive --new-file v1.3.59/linux/init/main.c linux/init/main.c
--- v1.3.59/linux/init/main.c	Fri Jan 26 01:37:07 1996
+++ linux/init/main.c	Thu Jan 25 20:54:42 1996
@@ -67,6 +67,7 @@
 extern void t128_setup(char *str, int *ints);
 extern void pas16_setup(char *str, int *ints);
 extern void generic_NCR5380_setup(char *str, int *intr);
+extern void generic_NCR53C400_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
@@ -227,6 +228,7 @@
 #endif
 #ifdef CONFIG_SCSI_GENERIC_NCR5380
 	{ "ncr5380=", generic_NCR5380_setup },
+	{ "ncr53c400=", generic_NCR53C400_setup },
 #endif
 #ifdef CONFIG_SCSI_AHA152X
 	{ "aha152x=", aha152x_setup},
diff -u --recursive --new-file v1.3.59/linux/ipc/msg.c linux/ipc/msg.c
--- v1.3.59/linux/ipc/msg.c	Tue Jan 23 21:15:54 1996
+++ linux/ipc/msg.c	Wed Feb  7 08:55:43 1996
@@ -102,7 +102,8 @@
 	}
 	
 	/* allocate message header and text space*/ 
-	msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER);
+	msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz,
+		(intr_count ? GFP_ATOMIC : GFP_USER));
 	if (!msgh)
 		return -ENOMEM;
 	msgh->msg_spot = (char *) (msgh + 1);
@@ -605,6 +606,13 @@
 	struct kerneld_msg kmsp = { msgtype, 0, (char *)text };
 	int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
 
+	if (kerneld_msqid == -1)
+		return -ENODEV;
+
+	/* Do not wait for an answer at interrupt-time! */
+	if (intr_count)
+		ret_size &= ~KERNELD_WAIT;
+
 	msgsz += sizeof(long);
 	if (ret_size & KERNELD_WAIT) {
 		if (++id <= 0)
@@ -612,22 +620,9 @@
 		kmsp.id = id;
 	}
 
-	if (kerneld_msqid == -1)
-		return -ENODEV;
-
 	status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
 	if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
 		ret_size &= ~KERNELD_WAIT;
-		if (intr_count) {
-			/*
-			 * Do not wait for an answer at interrupt-time!
-			 * OK, so fake it...
-			 * If the kerneld request failed in user-space
-			 * we will find out eventually, and retry again!
-			 */
-			return 0; /* i.e. say that it worked... */
-		}
-		/* else */
 		kmsp.text = (char *)ret_val;
 		status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
 				sizeof(long) + ((ret_val)?ret_size:0),
diff -u --recursive --new-file v1.3.59/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.59/linux/kernel/ksyms.c	Fri Jan 26 01:37:08 1996
+++ linux/kernel/ksyms.c	Wed Feb  7 08:55:43 1996
@@ -41,7 +41,9 @@
 #include <linux/mount.h>
 #include <linux/pagemap.h>
 #include <linux/sysctl.h>
+#include <linux/skbuff.h>
 #include <linux/genhd.h>
+#include <linux/swap.h>
 
 extern unsigned char aux_device_present, kbd_read_mask;
 
@@ -50,7 +52,6 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/firewall.h>
-
 #include <linux/trdevice.h>
 
 #ifdef CONFIG_AX25
@@ -64,6 +65,7 @@
 #include <net/ip.h>
 #include <net/udp.h>
 #include <net/tcp.h>
+#include <net/icmp.h>
 #include <net/route.h>
 #include <linux/net_alias.h>
 #endif
@@ -197,6 +199,7 @@
 	X(__bforget),
 	X(ll_rw_block),
 	X(__wait_on_buffer),
+	X(mark_buffer_uptodate),
 	X(unlock_buffer),
 	X(dcache_lookup),
 	X(dcache_add),
@@ -204,6 +207,7 @@
 	X(add_blkdev_randomness),
 	X(generic_file_read),
 	X(generic_readpage),
+	X(mark_buffer_uptodate),
 
 	/* device registration */
 	X(register_chrdev),
@@ -352,6 +356,8 @@
 	X(rarp_ioctl_hook),
 	X(init_etherdev),
 	X(ip_rt_route),
+	X(icmp_send),
+	X(ip_options_compile),
 	X(ip_rt_put),
 	X(arp_send),
 #ifdef CONFIG_IP_FORWARD
@@ -395,10 +401,10 @@
 	X(eth_copy_and_sum),
 	X(alloc_skb),
 	X(kfree_skb),
+	X(skb_clone),
 	X(dev_alloc_skb),
 	X(dev_kfree_skb),
 	X(netif_rx),
-	X(dev_rint),
 	X(dev_tint),
 	X(irq2dev_map),
 	X(dev_add_pack),
@@ -412,6 +418,9 @@
 	X(n_tty_ioctl),
 	X(tty_register_ldisc),
 	X(kill_fasync),
+#ifdef CONFIG_FIREWALL
+	X(call_in_firewall),
+#endif
 #endif
 #ifndef CONFIG_SCSI
 	/*
@@ -431,9 +440,11 @@
 	X(file_fsync),
 	X(clear_inode),
 	X(refile_buffer),
+	X(nr_async_pages),
 	X(___strtok),
 	X(init_fifo),
 	X(super_blocks),
+	X(reuse_list),
 	X(chrdev_inode_operations),
 	X(blkdev_inode_operations),
 	X(read_ahead),
diff -u --recursive --new-file v1.3.59/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.59/linux/kernel/sched.c	Tue Jan 23 21:15:54 1996
+++ linux/kernel/sched.c	Wed Feb  7 08:55:43 1996
@@ -331,6 +331,13 @@
 	 next->last_processor = this_cpu;
 	 
 #endif	 
+#ifdef __SMP_PROF__ 
+        /* mark processor running an idle thread */
+        if (0==next->pid)
+                set_bit(this_cpu,&smp_idle_map);
+        else
+                clear_bit(this_cpu,&smp_idle_map);
+#endif
 	if (current != next) {
 		struct timer_list timer;
 
@@ -742,6 +749,9 @@
 	unsigned long mask;
 	struct timer_struct *tp;
 	long ltemp, psecs;
+#ifdef  __SMP_PROF__
+        int cpu,i;
+#endif
 
 	/* Advance the phase, once it gets to one microsecond, then
 	 * advance the tick more.
@@ -790,6 +800,12 @@
 
 	jiffies++;
 	calc_load();
+#ifdef  __SMP_PROF__
+        smp_idle_count[NR_CPUS]++;    /* count timer ticks */
+        cpu = smp_processor_id();
+        for (i=0;i<(0==smp_num_cpus?1:smp_num_cpus);i++) 
+        	if (test_bit(i,&smp_idle_map)) smp_idle_count[i]++;
+#endif
 	if (user_mode(regs)) {
 		current->utime++;
 		if (current->pid) {
@@ -970,7 +986,7 @@
 
 	error = verify_area(VERIFY_READ, param, sizeof(struct sched_param));
 	if (error)
-		return -EINVAL;
+		return error;
         memcpy_fromfs(&lp, param, sizeof(struct sched_param));
 
 	p = find_process_by_pid(pid);
@@ -1041,7 +1057,7 @@
 
 	error = verify_area(VERIFY_WRITE, param, sizeof(struct sched_param));
 	if (error)
-		return -EINVAL;
+		return error;
 
 	p = find_process_by_pid(pid);
 	if (!p)
@@ -1092,7 +1108,7 @@
 
 	error = verify_area(VERIFY_WRITE, interval, sizeof(struct timespec));
 	if (error)
-		return -EINVAL;
+		return error;
 	
 	t.tv_sec = 0;
 	t.tv_nsec = 0;   /* <-- Linus, please fill correct value in here */
diff -u --recursive --new-file v1.3.59/linux/kernel/softirq.c linux/kernel/softirq.c
--- v1.3.59/linux/kernel/softirq.c	Wed Feb  1 21:46:08 1995
+++ linux/kernel/softirq.c	Wed Feb  7 08:04:32 1996
@@ -8,6 +8,9 @@
  * bottom_half handler need not be re-entrant.
  */
 
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
@@ -21,8 +24,6 @@
 #include <asm/irq.h>
 #include <asm/bitops.h>
 
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
 
 unsigned long intr_count = 0;
 
diff -u --recursive --new-file v1.3.59/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v1.3.59/linux/kernel/sysctl.c	Tue Jan 23 21:15:55 1996
+++ linux/kernel/sysctl.c	Wed Feb  7 08:55:43 1996
@@ -121,6 +121,8 @@
 	 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
 	{VM_KSWAPD, "kswapd", 
 	 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
+	{VM_FREEPG, "freepages", 
+	 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
 	{0}
 };
 
diff -u --recursive --new-file v1.3.59/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.59/linux/mm/filemap.c	Fri Jan 26 01:37:08 1996
+++ linux/mm/filemap.c	Wed Feb  7 08:55:43 1996
@@ -90,6 +90,8 @@
 	priority = (limit<<2) >> priority;
 	page = mem_map + clock;
 	while (priority-- > 0) {
+		if (page->locked)
+		    goto next;
 		/* First of all, regenerate the page's referenced bit
                    from any buffers in the page */
 		bh = buffer_pages[MAP_NR(page_address(page))];
@@ -113,7 +115,7 @@
 			page->referenced = 1;
 		else if (page->referenced)
 			page->referenced = 0;
-		else if (page->count && !page->locked) {
+		else if (page->count) {
 			/* The page is an old, unshared page --- try
                            to discard it. */
 			if (page->inode) {
@@ -125,6 +127,7 @@
 			if (bh && try_to_free_buffer(bh, &bh, 6))
 				return 1;
 		}
+next:
 		page++;
 		clock++;
 		if (clock >= limit) {
@@ -136,7 +139,7 @@
 }
 
 /*
- * This is called from try_to_swap_out() when we try to egt rid of some
+ * This is called from try_to_swap_out() when we try to get rid of some
  * pages..  If we're unmapping the last occurrence of this page, we also
  * free it from the page hash-queues etc, as we don't want to keep it
  * in-core unnecessarily.
diff -u --recursive --new-file v1.3.59/linux/mm/page_io.c linux/mm/page_io.c
--- v1.3.59/linux/mm/page_io.c	Fri Jan 26 01:37:08 1996
+++ linux/mm/page_io.c	Wed Feb  7 08:55:43 1996
@@ -105,7 +105,6 @@
 			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
 				if (!(zones[i] = bmap(swapf,block++))) {
 					printk("rw_swap_page: bad swap file\n");
-					return;
 				}
 		}
 		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
diff -u --recursive --new-file v1.3.59/linux/mm/swapfile.c linux/mm/swapfile.c
--- v1.3.59/linux/mm/swapfile.c	Fri Jan 26 01:37:08 1996
+++ linux/mm/swapfile.c	Wed Feb  7 08:55:43 1996
@@ -31,6 +31,7 @@
 
 struct swap_info_struct swap_info[MAX_SWAPFILES];
 
+
 static inline int scan_swap_map(struct swap_info_struct *si)
 {
 	int offset;
@@ -85,16 +86,20 @@
 		p = &swap_info[type];
 		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
 			offset = scan_swap_map(p);
-			if (!offset)
-				continue;
-			entry = SWP_ENTRY(type,offset);
-			type = swap_info[type].next;
-			if (type < 0 || p->prio != swap_info[type].prio) {
-				swap_list.next = swap_list.head;
-			} else {
-				swap_list.next = type;
+			if (offset) {
+				entry = SWP_ENTRY(type,offset);
+				type = swap_info[type].next;
+				if (type < 0 ||
+					p->prio != swap_info[type].prio) 
+				{
+						swap_list.next = swap_list.head;
+				}
+				else
+				{
+					swap_list.next = type;
+				}
+				return entry;
 			}
-			return entry;
 		}
 		type = p->next;
 		if (!wrapped) {
diff -u --recursive --new-file v1.3.59/linux/mm/vmscan.c linux/mm/vmscan.c
--- v1.3.59/linux/mm/vmscan.c	Fri Jan 26 01:37:08 1996
+++ linux/mm/vmscan.c	Wed Feb  7 08:55:43 1996
@@ -85,7 +85,7 @@
 		return 0;
 
 	page_map = mem_map + MAP_NR(page);
-	if (page_map->reserved)
+	if (page_map->reserved || page_map->locked)
 		return 0;
 	/* Deal with page aging.  Pages age from being unused; they
 	 * rejuvinate on being accessed.  Only swap old pages (age==0
diff -u --recursive --new-file v1.3.59/linux/net/Changes linux/net/Changes
--- v1.3.59/linux/net/Changes	Thu Jan  4 21:54:59 1996
+++ linux/net/Changes	Wed Feb  7 08:55:43 1996
@@ -21,9 +21,9 @@
 o	Routing cache (only in ip_build_header so far)	[TESTED]
 ------->>>>>  ALPHA 001  <<<<<----------
 o	eql load balancing driver.			[TESTED]
-o	Token ring drivers.				[NOW WORK APPARENTLY]
+o	Token ring drivers.				[TESTED]
 o	IPIP and tunnels				[TESTED]
-o	Fix ethernet/token ring promisc broadcast error [QUICK TEST]
+o	Fix ethernet/token ring promisc broadcast error [TESTED]
 	(pkt_type set to OTHERHOST in error).
 o	Fixed bug in the routing caches			[TESTED]
 o	Protocol header cache support			[TESTED]
@@ -46,7 +46,7 @@
 o	TCP SWS probe fix				[TESTED]
 o	Appletalk DDP					[TESTED]
 o	IP firewall bug fixed				[TESTED]
-o	IP masquerade ftp port spoof			[NOW WORKS]
+o	IP masquerade ftp port spoof			[TESTED]
 o	gcc 2.6.3 -O3 fix for checksum assembler	[TESTED]
 o	/proc support shows both timers			[TESTED]
 o	TCP irtt support				[TESTED]
@@ -84,7 +84,7 @@
 ////////////////////////////1.3.0/////////////////////////// 
 
 
-o	Merged loadable firewall code			[NOW IN (1.3.38)]
+o	Merged loadable firewall code			[TESTED]
 o	New buffers used totally non optimally		[TESTED]
 o	Fast ip_forwarding (needs changing)		[NOW INCLUDED IN 1.3.15]
 o	Fixed connection hang bug in new SWS code	[TESTED]
@@ -149,7 +149,7 @@
 o	Fast IP forwarding part 1 works			[CHECKED]
 
 ------->>>>>   1.3.15  <<<<<---------
-o	Mike Shaver has started RFC1122 verification	[IN PROGRESS]
+o	Mike Shaver has started RFC1122 verification	[DONE]
 o	Minor bug fixes					[TESTED]
 
 ------->>>> 1.3.16 <<<--------
@@ -184,9 +184,9 @@
 o	IP firewalling spoofing protection		[TESTED]
 o	IGMP bug fixes and workarounds			[TESTED]
 o	IFF_ALLMULTI protocol layer support		[TESTED]
-o	First parts of IP multicast routing code	[IN]
+o	First parts of IP multicast routing code	[TESTED]
 o	Generate BSD ENETDOWN errors			[TESTED]
-o	Clean device unload bug<Walter Wolfgang>	[NOW WORKS]
+o	Clean device unload bug<Walter Wolfgang>	[TESTED]
 
 -------->>>>> 1.3.24 <<<<<-------
 
@@ -201,7 +201,7 @@
 o	ARP PERM is really permanent now <Craig>	[TESTED]
 o	IPIP tunnels work fully we hope			[UMM...]
 o	Closing socket change (Marc Tamsky)		[TESTED]
-o	RFC1122 verification of tcp.c <Mike Shaver>	[IN]
+o	RFC1122 verification of tcp.c <Mike Shaver>	[DONE]
 
 -------->>>>> 1.3.26 <<<<<-------
 
@@ -271,12 +271,12 @@
 o	set_mac_addr fixed up				[TESTED]
 o	BSD SIOCSIFADDR, AF_UNSPEC behaviour		[TESTED]
 o	Updated this list				[OK]
-o	Massive ARP/cache/routing rewrite [ANK]		[IN]
+o	Massive ARP/cache/routing rewrite [ANK]		[TESTED]
 o	AX.25 connect return fixed in using sock_error	[TESTED]
 o	Proper netlink device major(36)			[TESTED]
 o	First parts of the SKIP support			[IN, not useful]
 o	TCP ICMP (SOSS should work again)		[TESTED]
-o	IPFW support for TOS changing (Al Longyear)	[IN]
+o	IPFW support for TOS changing (Al Longyear)	[TESTED]
 o	DECNET PPP test code [Steve]			[IN]
 o	NFS root	[Miguel/Gero]			[TESTED]
 o	Path MTU discovery [ANK]			[TESTED]
@@ -296,9 +296,9 @@
 -------->>>>> 1.3.46 <<<<<<--------
 
 o	AX.25/NetROM fixes/changes [John Naylor]	[TESTED]
-o	Further attempts to fix the IPX memory bug	[IN]
-o	ARP fixes (Assorted)				[IN]
-o	Driver fixes for multicast lists		[IN]
+o	Further attempts to fix the IPX memory bug	[NOW FIXED]
+o	ARP fixes (Assorted)				[TESTED]
+o	Driver fixes for multicast lists		[TESTED]
 
 -------->>>>> 1.3.48 <<<<<<--------
 
@@ -306,21 +306,36 @@
 
 -------->>>>> 1.3.50 <<<<<<--------
 
-o	TCP soft error support				[IN]
+o	TCP soft error support				[TESTED]
 o	Further 3c501 tweaking				[TESTED]
-o	Still trying to make IPX work right		[IN]
-o	Trap faulty boxes sending IGMP using 0.0.0.0	[IN]
-o	Only allow SMBFS selection with IP configured	[IN]
+o	Still trying to make IPX work right		[TESTED]
+o	Trap faulty boxes sending IGMP using 0.0.0.0	[TESTED]
+o	Only allow SMBFS selection with IP configured	[TESTED]
 o	Packetwin driver [Craig]			[IN]
-o	Net alias changes [Juan]			[IN]
+o	Net alias changes [Juan]			[TESTED]
 
 -------->>>>> 1.3.53 <<<<<<--------
 
-o	Missing htons() in ip_build_xmit [Mike Kilburn]	[IN]
-o	Missing protocol type set in appletalk		[IN]
-o	Net alias changes/documentation [Juan Ciarlante][IN]
-o	Set protocol type on IPX sends [Various]	[IN]
-o	Lance driver packet length sanity check		[IN]
+o	Missing htons() in ip_build_xmit [Mike Kilburn]	[TESTED]
+o	Missing protocol type set in appletalk		[TESTED]
+o	Net alias changes/documentation [Juan Ciarlante][TESTED]
+o	Set protocol type on IPX sends [Various]	[TESTED]
+o	Lance driver packet length sanity check		[TESTED]
+
+-------->>>>> 1.3.60 <<<<<<--------
+
+o	Fixed NFS notice message			[IN]
+o	Extra ETH_P_xx types				[IN]
+o	Added skb_copy					[IN]
+o	AX.25 unlock bug fix [Joerg]			[IN]
+o	Disabled buggy kerneld support			[IN]
+o	Removed dev_rint				[IN]
+o	Fixed bind checking				[IN]
+o	ARP changes [Bernd]				[IN]
+o	IPX memory leak fix [Martin]			[IN]
+o	Net builds without /proc [Paul]			[IN]
+o	IP multicast races removed			[IN]
+o	Device queue length limits and packet discarder [IN]
 
 ---------- Things I thought Linus had for a while and not merged ----------------
 
@@ -343,12 +358,9 @@
 
 --------------- Things That Need Doing Before 1.4 ------------------
 
-o	Forwarding queue control (+ fairness algorithms ??)
-o	IP forward flow control.
-o	IPX memory leak ?????				[Done with luck]
 o	Clean up RAW AX.25 sockets.			[Sorted by skb_clone change]
 o	Finish IPIP bug fixes				[Done hopefully]
-o	Multicast routing				[STARTED BITS]
+o	Multicast routing				[Nearly right]
 o	PPP/IPX
 o	IPX for Lwared
 o	SKIP						[Available in user mode]
@@ -360,21 +372,19 @@
 
 o	signal interrupting a unix domain connect can occasionally hang the 
 	machine ??
-o	IPX has a memory accounting bug.		[HOPE DONE]
 o	TCP socket cache gets things wrong very very occasionally under high 
 	load.						[TRYING THINGS]
 o	AX.25/NetROM needs more locking.
-o	IP mroute code Oopses still.			[WAITING DIFF]
-o	Lance driver in a few rare systems is causing crashes in copy/checksum.
-o	NFS logs an error (-512) when interrupted.
 o	NFS flow control is needed with the new multirequest NFS support.
 o	Need to be able to turn off the intelligent arp refreshing as its not so
 	hot over AX.25 and upsets some people with very dumb ISDN bridges.
+o	Matti Arnio's TCP problem.
+o	Should unix domain connect never block ?
+o	Sort out kerneld getting things right.
 
 0.2
 ---
 o	Fast checksum/copy on outgoing TCP
-o	Better TCP window handling [Pedro Roque]
 o	Add tty support to sonix driver.
 o	PPP for Sonix ISDN.
 o	Screend loadable firewall module
@@ -389,9 +399,7 @@
 
 0.3
 ---
-o	Merge the layered protocol support.		[ABANDONED TOO SLOW]
 o	802.2 Class 2 services (eg netbios).
-o	Multidrop KISS					[NOW AVAILABLE IN USER MODE]
 
 Possible projects for victim^H^H^H^H^Holunteers
 
@@ -416,7 +424,7 @@
 that two acks can be merged into one or for interactive use the ack can
 piggyback on the next character typed (great improvement on 2400 baud
 modems). Johannes Stille did some work on this about 0.99.13 but it never
-got merged in.
+got merged in. [Pedro Roque]
 
 7.	One on my tempting project list. Add an extra (unofficial - but so
 is SLIP6) SLIP mode that does packet data compression [maybe use the code
@@ -424,7 +432,7 @@
 
 9.	Implementing streams. Not as a blind slow SYS5.4 style copy but actually
 working out how to do it so it runs like greased lightning. Quite a big
-problem.
+problem. [See the LiS project]
 
 10.	Frame Relay/WAN/ISDN drivers [I'm working on the sonix EuroISDN board
 driver but thats for an internal project and its general release is still
@@ -452,7 +460,7 @@
 [Taken]
 
 20.	SKIP IP security using ENskip-0.10 - started
-[Me]
+[Abandoned]
 
 21.	T/TCP support.
 
@@ -462,5 +470,3 @@
 use of gdb on the kernel (no breakpoints though!). No magic involved.
 
 Alan
-
-
diff -u --recursive --new-file v1.3.59/linux/net/Config.in linux/net/Config.in
--- v1.3.59/linux/net/Config.in	Tue Jan 23 21:15:55 1996
+++ linux/net/Config.in	Wed Feb  7 08:55:43 1996
@@ -9,7 +9,11 @@
 if [ "$CONFIG_INET" = "y" ]; then
   source net/ipv4/Config.in
 fi
+comment ' '
 bool 'The IPX protocol' CONFIG_IPX
+if [ "$CONFIG_IPX" = "y" ]; then
+  bool 'Full internal IPX network' CONFIG_IPX_INTERN
+fi
 bool 'Appletalk DDP' CONFIG_ATALK
 bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" = "y" ]; then
diff -u --recursive --new-file v1.3.59/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.59/linux/net/appletalk/ddp.c	Wed Nov  8 07:11:44 1995
+++ linux/net/appletalk/ddp.c	Wed Feb  7 08:55:43 1996
@@ -17,6 +17,7 @@
  *		Alan Cox		:	Moved at. to protinfo in
  *						socket.
  *		Alan Cox		:	Added firewall hooks.
+ *		Alan Cox		:	Supports new ARPHRD_LOOPBACK
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -714,7 +715,7 @@
 				return -EPERM;
 			if(sa->sat_family!=AF_APPLETALK)
 				return -EINVAL;
-			if(dev->type!=ARPHRD_ETHER)
+			if(dev->type!=ARPHRD_ETHER&&dev->type!=ARPHRD_LOOPBACK)
 				return -EPROTONOSUPPORT;
 			nr=(struct netrange *)&sa->sat_zero[0];
 			if(nr->nr_phase!=2)
@@ -1032,6 +1033,7 @@
 		return err;
 	put_user(sizeof(int),optlen);
 	err=verify_area(VERIFY_WRITE,optval,sizeof(int));
+	if (err) return err;
 	put_user(val,optval);
 	return(0);
 }
diff -u --recursive --new-file v1.3.59/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.59/linux/net/ax25/af_ax25.c	Mon Dec 11 15:42:08 1995
+++ linux/net/ax25/af_ax25.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -68,6 +68,10 @@
  *						context, THEN make the sock dead.
  *			Alan(GW4PTS)		Cleaned up for single recvmsg methods.
  *			Alan(GW4PTS)		Fixed not clearing error on connect failure.
+ *	AX.25 031	Jonathan(G4KLX)		Added binding to any device.
+ *			Joerg(DL1BKE)		Added DAMA support, fixed (?) digipeating, fixed buffer locking
+ *						for "virtual connect" mode... Result: Probably the
+ *						"Most Buggiest Code You've Ever Seen" (TM)
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
@@ -107,14 +111,13 @@
 #include <net/ip.h>
 #include <net/arp.h>
 
+/*
+ *	The null address is defined as a callsign of all spaces with an
+ *	SSID of zero.
+ */
+ax25_address null_ax25_address = {{0x40,0x40,0x40,0x40,0x40,0x40,0x00}};
 
-/**********************************************************************************************************************\
-*														       *
-*						Handlers for the socket list.					       *
-*														       *
-\**********************************************************************************************************************/
-
-static ax25_cb *volatile ax25_list = NULL;
+ax25_cb *volatile ax25_list = NULL;
 
 /*
  *	ax25 -> ascii conversion
@@ -261,7 +264,7 @@
 }
 
 /*
- *	Find a socket that wants to accept the SABM we just
+ *	Find a socket that wants to accept the SABM we have just
  *	received.
  */
 static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, int type)
@@ -422,11 +425,6 @@
 		}
 	}
 	
-	if (ax25->digipeat != NULL) {
-		kfree_s(ax25->digipeat, sizeof(ax25_digi));
-		ax25->digipeat = NULL;
-	}
-
 	if (ax25->sk != NULL) {
 		if (ax25->sk->wmem_alloc || ax25->sk->rmem_alloc) { /* Defer: outstanding buffers */
 			init_timer(&ax25->timer);
@@ -435,10 +433,19 @@
 			ax25->timer.data     = (unsigned long)ax25;
 			add_timer(&ax25->timer);
 		} else {
+			if (ax25->digipeat != NULL) {
+				kfree_s(ax25->digipeat, sizeof(ax25_digi));
+				ax25->digipeat = NULL;
+			}
+		
 			kfree_s(ax25->sk, sizeof(*ax25->sk));
 			kfree_s(ax25, sizeof(*ax25));
 		}
 	} else {
+		if (ax25->digipeat != NULL) {
+			kfree_s(ax25->digipeat, sizeof(ax25_digi));
+			ax25->digipeat = NULL;
+		}	
 		kfree_s(ax25, sizeof(*ax25));
 	}
 
@@ -530,6 +537,8 @@
 
 	init_timer(&ax25->timer);
 
+	ax25->dama_slave = 0;	/* dl1bke 951121 */
+
 	ax25->rtt     = (AX25_DEF_T1 * PR_SLOWHZ) / 2;
 	ax25->t1      = AX25_DEF_T1 * PR_SLOWHZ;
 	ax25->t2      = AX25_DEF_T2 * PR_SLOWHZ;
@@ -569,6 +578,28 @@
 }
 
 /*
+ *	Find out if we are a DAMA slave for this device and count the
+ *	number of connections.
+ *
+ *	dl1bke 951121
+ */
+ 
+int ax25_dev_is_dama_slave(struct device *dev)
+{
+	ax25_cb * ax25;
+	int count = 0;
+	
+	for (ax25=ax25_list; ax25 ; ax25 = ax25->next)
+		if ( (ax25->device == dev) && ax25->dama_slave )
+		{
+			count++;
+			break;
+		}
+		
+	return count;
+}
+
+/*
  *	Fill in a created AX.25 created control block with the default
  *	values for a particular device.
  */
@@ -582,6 +613,8 @@
 	ax25->t3  = ax25_dev_get_value(dev, AX25_VALUES_T3);
 	ax25->n2  = ax25_dev_get_value(dev, AX25_VALUES_N2);
 
+	ax25->dama_slave = 0;
+
 	ax25->modulus = ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE);
 
 	if (ax25->modulus == MODULUS) {
@@ -628,9 +661,15 @@
 			return 0;
 		}
 		memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
+	} else {
+		ax25_rt_build_path(ax25, dest);
 	}
 
+	if (ax25_dev_is_dama_slave(ax25->device))	/* dl1bke 960116 */
+		dama_establish_data_link(ax25);
+	else
 	ax25_establish_data_link(ax25);
+		
 	ax25_insert_socket(ax25);
 
 	ax25->state = AX25_STATE_1;
@@ -642,12 +681,9 @@
 	return 1;			/* We had to create it */	
 }
 
-/*******************************************************************************************************************\
-*														    *
-*		Routing rules for AX.25: Basically iterate over the active interfaces 				    *
-*														    *
-\*******************************************************************************************************************/
-
+/*
+ *	Find the AX.25 device that matches the hardware address supplied.
+ */
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
 	struct device *dev;
@@ -678,19 +714,14 @@
 	return NULL;
 }
 
-/*******************************************************************************************************************\
-*													            *
-*	      Handling for system calls applied via the various interfaces to an AX25 socket object		    *
-*														    *
-\*******************************************************************************************************************/
+/*
+ *	Handling for system calls applied via the various interfaces to an
+ *	AX25 socket object
+ */
  
 static int ax25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	switch(cmd)
-	{
-		default:
-			return(-EINVAL);
-	}
+	return -EINVAL;
 }
 
 static int ax25_setsockopt(struct socket *sock, int level, int optname,
@@ -1028,6 +1059,9 @@
 			kfree_s(ax25, sizeof(*ax25));
 			return NULL;
 		}
+		
+		/* dl1bke 960119: we have to copy the old digipeater list! */
+		memcpy(ax25->digipeat, osk->ax25->digipeat, sizeof(ax25_digi));
 	}
 
 	sk->ax25 = ax25;
@@ -1068,6 +1102,9 @@
 				break;
 
 			case AX25_STATE_2:
+				if (sk->ax25->dama_slave)
+					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
+				else
 				ax25_send_control(sk->ax25, DM, POLLON, C_RESPONSE);
 				sk->ax25->state = AX25_STATE_0;
 				sk->state       = TCP_CLOSE;
@@ -1080,6 +1117,7 @@
 			case AX25_STATE_4:
 				ax25_clear_queues(sk->ax25);
 				sk->ax25->n2count = 0;
+				if (!sk->ax25->dama_slave)
 				ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
 				sk->ax25->t3timer = 0;
 				sk->ax25->t1timer = sk->ax25->t1 = ax25_calculate_t1(sk->ax25);
@@ -1127,14 +1165,6 @@
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
-#ifdef DONTDO
-	if (ax25_find_socket(&addr->fsa_ax25.sax25_call, sk->type) != NULL) {
-		if (sk->debug)
-			printk("AX25: bind failed: in use\n");
-		return -EADDRINUSE;
-	}
-#endif
-
 	call = ax25_findbyuid(current->euid);
 	if (call == NULL && ax25_uid_policy && !suser())
 		return -EPERM;
@@ -1144,18 +1174,31 @@
 	else
 		memcpy(&sk->ax25->source_addr, call, sizeof(ax25_address));
 
+	if (sk->debug)
+		printk("AX25: source address set to %s\n", ax2asc(&sk->ax25->source_addr));
+
 	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
-		if (!suser())
-			return -EPERM;
-		call = &addr->fsa_digipeater[0];
+		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
+			dev = NULL;
+			if (sk->debug)
+				printk("AX25: bound to any device\n");
+		} else {
+			if ((dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) {
+				if (sk->debug)
+					printk("AX25: bind failed - no device\n");
+				return -EADDRNOTAVAIL;
+			}
+			if (sk->debug)
+				printk("AX25: bound to device %s\n", dev->name);
+		}
 	} else {
-		call = &addr->fsa_ax25.sax25_call;
-	}
-
-	if ((dev = ax25rtr_get_dev(call)) == NULL) {
+		if ((dev = ax25rtr_get_dev(&addr->fsa_ax25.sax25_call)) == NULL) {
+			if (sk->debug)
+				printk("AX25: bind failed - no device\n");
+			return -EADDRNOTAVAIL;
+		}
 		if (sk->debug)
-			printk("AX25 bind failed: no device\n");
-		return -EADDRNOTAVAIL;
+			printk("AX25: bound to device %s\n", dev->name);
 	}
 
 	ax25_fillin_cb(sk->ax25, dev);
@@ -1194,6 +1237,10 @@
 
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
+
+	/*
+	 *	Handle digi-peaters to be used.
+	 */
 	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) {
 		int ct           = 0;
 		struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr;
@@ -1217,11 +1264,26 @@
 
 		sk->ax25->digipeat->lastrepeat = 0;
 	}
+	else
+	{ /* dl1bke 960117 */
+		if (sk->debug)
+			printk("building digipeater path\n");
+		ax25_rt_build_path(sk->ax25, &addr->sax25_call);
+	}
 
-	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
+	/*
+	 *	Must bind first - autobinding in this may or may not work. If
+	 *	the socket is already bound, check to see if the device has
+	 *	been filled in, error if it hasn't.
+	 */
+	if (sk->zapped) {
 		if ((err = ax25_rt_autobind(sk->ax25, &addr->sax25_call)) < 0)
 			return err;
-		ax25_insert_socket(sk->ax25);		/* Finish the bind */
+		ax25_fillin_cb(sk->ax25, sk->ax25->device);
+		ax25_insert_socket(sk->ax25);
+	} else {
+		if (sk->ax25->device == NULL)
+			return -EHOSTUNREACH;
 	}
 		
 	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->ax25->source_addr, &addr->sax25_call, sk->ax25->device) != NULL)
@@ -1239,7 +1301,12 @@
 	/* Move to connecting socket, ax.25 lapb WAIT_UA.. */	
 	sock->state        = SS_CONNECTING;
 	sk->state          = TCP_SYN_SENT;
+	
+	if (ax25_dev_is_dama_slave(sk->ax25->device))
+		dama_establish_data_link(sk->ax25);
+	else
 	ax25_establish_data_link(sk->ax25);
+		
 	sk->ax25->state     = AX25_STATE_1;
 	ax25_set_timer(sk->ax25);		/* Start going SABM SABM until a UA or a give up and DM */
 	
@@ -1297,8 +1364,10 @@
 	if (sk->state != TCP_LISTEN)
 		return -EINVAL;
 		
-	/* The write queue this time is holding sockets ready to use
-	   hooked into the SABM we saved */
+	/*
+	 *	The write queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
 	do {
 		cli();
 		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
@@ -1372,6 +1441,7 @@
 	ax25_address src, dest;
 	struct sock *raw;
 	int mine = 0;
+	int dama;
 
 	/*
 	 *	Process the AX.25/LAPB frame.
@@ -1392,7 +1462,7 @@
 	 *	Parse the address header.
 	 */
 	 
-	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type) == NULL) {
+	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -1427,11 +1497,17 @@
 					dev_out = dev_scan;
 				}
 				if (dev != dev_out && (ax25_dev_get_value(dev_out, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) == 0)
+				{
 					kfree_skb(skb, FREE_READ);
+					return 0;
+				}
 			}
 
 			if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0)
+			{
 				kfree_skb(skb, FREE_READ);
+				return 0;	/* Hey, Alan: look what you're doing below! You forgot this return! */
+			}
 
 			build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
 #ifdef CONFIG_FIREWALL
@@ -1530,18 +1606,25 @@
 	}
 	
 	/* LAPB */
+	
+	/* AX.25 state 1-4 */
+	
 	if ((ax25 = ax25_find_cb(&dest, &src, dev)) != NULL) {
 		/*
 		 *	Process the frame. If it is queued up internally it returns one otherwise we 
 		 *	free it immediately. This routine itself wakes the user context layers so we
 		 *	do no further work
 		 */
-		if (ax25_process_rx_frame(ax25, skb, type) == 0)
+		if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
 			kfree_skb(skb, FREE_READ);
 
 		return 0;
 	}
 
+	/* AX.25 state 0 (disconnected) */
+
+	/* a) received not a SABM(E) */
+	
 	if ((*skb->data & ~PF) != SABM && (*skb->data & ~PF) != SABME) {
 		/*
 		 *	Never reply to a DM. Also ignore any connects for
@@ -1554,6 +1637,8 @@
 		return 0;
 	}
 
+	/* b) received SABM(E) */
+	
 	if ((sk = ax25_find_listener(&dest, dev, SOCK_SEQPACKET)) != NULL) {
 		if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
 			if (mine)
@@ -1629,6 +1714,9 @@
 	
 	ax25_send_control(ax25, UA, POLLON, C_RESPONSE);
 
+	if (dama) ax25_dama_on(ax25);	/* bke 951121 */
+
+	ax25->dama_slave = dama;
 	ax25->t3timer = ax25->t3;
 	ax25->state   = AX25_STATE_3;
 
@@ -1743,7 +1831,9 @@
 		if (sk->type == SOCK_SEQPACKET && memcmp(&sk->ax25->dest_addr, &sax.sax25_call, sizeof(ax25_address)) != 0)
 			return -EISCONN;
 		if (usax->sax25_ndigis == 0)
+		{
 			dp = NULL;
+		}
 		else
 			dp = &dtmp;
 	} else {
@@ -1833,6 +1923,7 @@
 	int copied, length;
 	struct sk_buff *skb;
 	int er;
+	int dama;
 
 	if (sk->err) {
 		return sock_error(sk);
@@ -1873,7 +1964,7 @@
 		if (*addr_len != sizeof(struct sockaddr_ax25) && *addr_len != sizeof(struct full_sockaddr_ax25))
 			return -EINVAL;
 
-		ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL);
+		ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
 
 		sax->sax25_family = AF_AX25;
 		/* We set this correctly, even though we may not let the
@@ -2099,7 +2190,9 @@
 	ax25_recvmsg
 };
 
-/* Called by socket.c on kernel start up */
+/*
+ *	Called by socket.c on kernel start up
+ */
 
 static struct packet_type ax25_packet_type = 
 {
@@ -2156,7 +2249,7 @@
 		ax25_cs_get_info
 	});
 
-	printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.30 BETA for Linux NET3.032 (Linux 1.3.35)\n");
+	printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.31 BETA for Linux NET3.032 (Linux 1.3.53)\n");
 
 #ifdef CONFIG_BPQETHER
 	proc_net_register(&(struct proc_dir_entry) {
@@ -2178,17 +2271,18 @@
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
 	unsigned char *ptr;
+	int was_locked;
 	
 #ifdef CONFIG_FIREWALL
-
 	if(call_out_firewall(PF_AX25, skb, skb->data)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_WRITE);
 		return;
-	}	
-	
+	}
 #endif	
+
 	skb->protocol = htons (ETH_P_AX25);
+
 #ifdef CONFIG_BPQETHER
 	if(dev->type == ARPHRD_ETHER)
 	{
@@ -2211,7 +2305,14 @@
 
 		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
 
+		/* dl1bke 960201: see below. Note that the device driver should 
+		 * 		  copy the data into its own buffers, or strange
+		 *                things will happen again.
+		 */
+
+		was_locked = skb_device_locked(skb);
 		dev_queue_xmit(skb, dev, pri);
+		if (was_locked) skb_device_unlock(skb);
 
 		return;
 	} 
@@ -2220,15 +2321,20 @@
 	ptr = skb_push(skb, 1);
 	*ptr++ = 0;			/* KISS */
 
+/* dl1bke 960201: dev_queue_xmit() will free the skb if it's not locked, so
+ *                we need an additional variable to store its status.
+ *		  sl_xmit() copies the data before returning, we can
+ *		  remove the lock savely.
+ */
+
+	was_locked = skb_device_locked(skb);
 	dev_queue_xmit(skb, dev, pri);
+	if (was_locked) skb_device_unlock(skb);
 }
 
-/*******************************************************************************************************************\
-*														    *
-*		Driver encapsulation support: Moved out of SLIP because a) it should be here 			    *
-*									b) for HDLC cards			    *
-*														    *
-\*******************************************************************************************************************/
+/*
+ *	IP over AX.25 encapsulation.
+ */
 
 /*
  *	Shove an AX.25 UI header on an IP packet and handle ARP
@@ -2274,6 +2380,7 @@
   			*buff++ = AX25_P_ARP;
   			break;
   		default:
+  			printk("wrong protocol type 0x%x2.2\n", type);
   			*buff++ = 0;
   			break;
  	}
@@ -2296,8 +2403,22 @@
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
 		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) 
 		{
-			skb_device_unlock(skb);
+/*			skb_device_unlock(skb); *//* Don't unlock - it might vanish.. TCP will respond correctly to this lock holding */
 			skb_pull(skb, AX25_HEADER_LEN - 1);	/* Keep PID */
+#ifdef HUNTING_FOR_ENCAP_BUG
+		/* dl1bke 960131: This is a weird bug: the AX.25 frame is encapsulated */
+		/* 		  twice... We'll try a work-around here and hope for   */
+		/* 		  the best.                                            */
+
+			if ( !(ax25cmp((ax25_address *) (bp + 8), (ax25_address *) (skb->data + 8)) ||
+			       ax25cmp((ax25_address *) (bp + 1), (ax25_address *) (skb->data + 1)) ) )
+			{
+				printk("ax25_rebuild_header(): encap bug...\n");
+				skb_pull(skb, AX25_HEADER_LEN);
+			} else
+				if (!*skb->data)
+					printk("ax25_rebuild_header(): probably encap bug...\n");
+#endif
 			ax25_send_frame(skb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
 			return 1;
 		}
diff -u --recursive --new-file v1.3.59/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v1.3.59/linux/net/ax25/ax25_in.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/ax25_in.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -26,6 +26,8 @@
  *			Jonathan(G4KLX)	Added IP mode registration.
  *	AX.25 030	Jonathan(G4KLX)	Added AX.25 fragment reception.
  *					Upgraded state machine for SABME.
+ *					Added arbitrary protocol id support.
+ *			Joerg(DL1BKE)	Added DAMA support
  */
 
 #include <linux/config.h>
@@ -129,8 +131,12 @@
  */
 static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
 {
-	int queued = 0;
-	unsigned char pid = *skb->data;
+	volatile int queued = 0;
+	unsigned char pid;
+	
+	if (skb == NULL) return 0;
+	
+	pid = *skb->data;
 
 	switch (pid) {
 #ifdef CONFIG_NETROM
@@ -146,8 +152,9 @@
 			skb_pull(skb, 1);	/* Remove PID */
 			skb->h.raw = skb->data;
 			ax25_ip_mode_set(&ax25->dest_addr, ax25->device, 'V');
-			ip_rcv(skb, skb->dev, NULL);	/* Wrong ptype */
+			ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
 			queued = 1;
+			
 			break;
 #endif
 		case AX25_P_SEGMENT:
@@ -174,7 +181,7 @@
  *	The handling of the timer(s) is in file ax25_timer.c.
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type, int dama)
 {
 	switch (frametype) {
 		case SABM:
@@ -194,7 +201,9 @@
 			break;
 
 		case UA:
-			if (pf) {
+			if (pf || dama) {
+				if (dama) ax25_dama_on(ax25); /* bke */
+					
 				ax25_calculate_rtt(ax25);
 				ax25->t1timer = 0;
 				ax25->t3timer = ax25->t3;
@@ -203,6 +212,8 @@
 				ax25->vr      = 0;
 				ax25->state   = AX25_STATE_3;
 				ax25->n2count = 0;
+				ax25->dama_slave = dama;	/* bke */
+					
 				if (ax25->sk != NULL) {
 					ax25->sk->state = TCP_ESTABLISHED;
 					/* For WAIT_SABM connections we will produce an accept ready socket here */
@@ -232,6 +243,8 @@
 			break;
 
 		default:
+			if (dama && pf)	/* dl1bke 960116 */
+				ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
 			break;
 	}
 
@@ -249,15 +262,35 @@
 		case SABM:
 		case SABME:
 			ax25_send_control(ax25, DM, pf, C_RESPONSE);
+			if (ax25->dama_slave)
+				ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
 			break;
 
 		case DISC:
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
+			if (ax25->dama_slave)
+			{
+				ax25->state = AX25_STATE_0;
+				ax25->dama_slave = 0;
+				ax25_dama_off(ax25);
+
+				if (ax25->sk != NULL) 
+				{
+					ax25->sk->state = TCP_CLOSE;
+					ax25->sk->err   = 0;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead  = 1;
+				}
+			}
 			break;
 
 		case UA:
 			if (pf) {
 				ax25->state = AX25_STATE_0;
+				ax25->dama_slave = 0;
+				ax25_dama_off(ax25);
+
 				if (ax25->sk != NULL) {
 					ax25->sk->state = TCP_CLOSE;
 					ax25->sk->err   = 0;
@@ -271,6 +304,9 @@
 		case DM:
 			if (pf) {
 				ax25->state = AX25_STATE_0;
+				ax25->dama_slave = 0;
+				ax25_dama_off(ax25);
+					
 				if (ax25->sk != NULL) {
 					ax25->sk->state = TCP_CLOSE;
 					ax25->sk->err   = 0;
@@ -286,7 +322,12 @@
 		case RNR:
 		case RR:
 			if (pf)
+			{
+				if (ax25->dama_slave)
+					ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+				else
 				ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
+			}
 			break;
 				
 		default:
@@ -301,12 +342,14 @@
  *	The handling of the timer(s) is in file ax25_timer.c
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
 {
 	int queued = 0;
 
 	switch (frametype) {
 		case SABM:
+			if (dama) ax25_dama_on(ax25);
+				
 			ax25->modulus   = MODULUS;
 			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
@@ -316,9 +359,12 @@
 			ax25->vs        = 0;
 			ax25->va        = 0;
 			ax25->vr        = 0;
+			ax25->dama_slave = dama;
 			break;
 
 		case SABME:
+			if (dama) ax25_dama_on(ax25);
+				
 			ax25->modulus   = EMODULUS;
 			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
@@ -328,6 +374,7 @@
 			ax25->vs        = 0;
 			ax25->va        = 0;
 			ax25->vr        = 0;
+			ax25->dama_slave = dama;
 			break;
 
 		case DISC:
@@ -335,6 +382,9 @@
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
+			ax25->dama_slave = 0;
+			ax25_dama_off(ax25);
+			
 			if (ax25->sk != NULL) {
 				ax25->sk->state = TCP_CLOSE;
 				ax25->sk->err   = 0;
@@ -348,6 +398,8 @@
 			ax25_clear_queues(ax25);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
+			ax25->dama_slave = 0;
+			ax25_dama_off(ax25);
 			if (ax25->sk) {
 				ax25->sk->state = TCP_CLOSE;
 				ax25->sk->err   = ECONNRESET;
@@ -362,6 +414,7 @@
 			ax25_check_need_response(ax25, type, pf);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_check_iframes_acked(ax25, nr);
+				dama_check_need_response(ax25, type, pf);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -373,6 +426,7 @@
 			ax25_check_need_response(ax25, type, pf);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_check_iframes_acked(ax25, nr);
+				dama_check_need_response(ax25, type, pf);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -388,6 +442,7 @@
 				ax25->t1timer = 0;
 				ax25->t3timer = ax25->t3;
 				ax25_requeue_frames(ax25);
+				dama_check_need_response(ax25, type, pf);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -410,18 +465,33 @@
 				ax25_check_iframes_acked(ax25, nr);
 			}
 			if (ax25->condition & OWN_RX_BUSY_CONDITION) {
-				if (pf) ax25_enquiry_response(ax25);
+				if (pf)
+				{
+					if (ax25->dama_slave)	/* dl1bke 960114 */
+						dama_enquiry_response(ax25);
+					else
+						ax25_enquiry_response(ax25);
+				}
 				break;
 			}
 			if (ns == ax25->vr) {
 				queued = ax25_rx_iframe(ax25, skb);
 				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
-					if (pf) ax25_enquiry_response(ax25);
+					if (pf)
+					{
+						if (ax25->dama_slave)	/* dl1bke 960114 */
+							dama_enquiry_response(ax25);
+						else
+							ax25_enquiry_response(ax25);
+					}
 					break;
 				}
 				ax25->vr = (ax25->vr + 1) % ax25->modulus;
 				ax25->condition &= ~REJECT_CONDITION;
 				if (pf) {
+					if (ax25->dama_slave)	/* dl1bke 960114 */
+						dama_enquiry_response(ax25);
+					else
 					ax25_enquiry_response(ax25);
 				} else {
 					if (!(ax25->condition & ACK_PENDING_CONDITION)) {
@@ -431,9 +501,18 @@
 				}
 			} else {
 				if (ax25->condition & REJECT_CONDITION) {
-					if (pf) ax25_enquiry_response(ax25);
+					if (pf) 
+					{
+						if (ax25->dama_slave)	/* dl1bke 960114 */
+							dama_enquiry_response(ax25);
+						else
+							ax25_enquiry_response(ax25);
+					}
 				} else {
 					ax25->condition |= REJECT_CONDITION;
+					if (ax25->dama_slave)		/* dl1bke 960114 */
+						dama_enquiry_response(ax25);
+					else
 					ax25_send_control(ax25, REJ, pf, C_RESPONSE);
 					ax25->condition &= ~ACK_PENDING_CONDITION;
 				}
@@ -458,12 +537,15 @@
  *	The handling of the timer(s) is in file ax25_timer.c
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
 {
 	int queued = 0;
 
 	switch (frametype) {
 		case SABM:
+			if (dama) ax25_dama_on(ax25);
+				
+			ax25->dama_slave = dama;
 			ax25->modulus   = MODULUS;
 			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
@@ -478,6 +560,9 @@
 			break;
 
 		case SABME:
+			if (dama) ax25_dama_on(ax25);
+				
+			ax25->dama_slave = dama;
 			ax25->modulus   = EMODULUS;
 			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
@@ -496,6 +581,9 @@
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
+			ax25->dama_slave = 0;
+			ax25_dama_off(ax25);
+			
 			if (ax25->sk != NULL) {
 				ax25->sk->state = TCP_CLOSE;
 				ax25->sk->err   = 0;
@@ -509,6 +597,9 @@
 			ax25_clear_queues(ax25);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
+			ax25->dama_slave = 0;
+			ax25_dama_off(ax25);
+			
 			if (ax25->sk != NULL) {
 				ax25->sk->state = TCP_CLOSE;
 				ax25->sk->err   = ECONNRESET;
@@ -535,10 +626,13 @@
 				}
 				break;
 			}
-			if (type == C_COMMAND && pf)
-				ax25_enquiry_response(ax25);
+			/* dl1bke 960114: replaced if(..) ax25_enquiry_response */
+			/* 		  with ax25_check_need_response()       */
+			 
+			ax25_check_need_response(ax25, type, pf);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
+				dama_check_need_response(ax25, type, pf);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -547,7 +641,7 @@
 			
 		case RR:
 			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			if (type == C_RESPONSE && pf) {
+			if ( pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND)) ) {
 				ax25->t1timer = 0;
 				if (ax25_validate_nr(ax25, nr)) {
 					ax25_frames_acked(ax25, nr);
@@ -558,16 +652,18 @@
 					} else {
 						ax25_requeue_frames(ax25);
 					}
+					dama_check_need_response(ax25, type, pf);
 				} else {
 					ax25_nr_error_recovery(ax25);
 					ax25->state = AX25_STATE_1;
 				}
 				break;
 			}
-			if (type == C_COMMAND && pf)
-				ax25_enquiry_response(ax25);
+
+			ax25_check_need_response(ax25, type, pf);	/* dl1bke 960114 */
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
+				dama_check_need_response(ax25, type, pf);	/* dl1bke 960114 */
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -576,7 +672,7 @@
 
 		case REJ:
 			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			if (type == C_RESPONSE && pf) {
+			if ( pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND)) ) {
 				ax25->t1timer = 0;
 				if (ax25_validate_nr(ax25, nr)) {
 					ax25_frames_acked(ax25, nr);
@@ -587,19 +683,21 @@
 					} else {
 						ax25_requeue_frames(ax25);
 					}
+					dama_check_need_response(ax25, type, pf);
 				} else {
 					ax25_nr_error_recovery(ax25);
 					ax25->state = AX25_STATE_1;
 				}
 				break;
 			}
-			if (type == C_COMMAND && pf)
-				ax25_enquiry_response(ax25);
+			
+			ax25_check_need_response(ax25, type, pf);	/* dl1bke 960114 */
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
 				if(ax25->vs != ax25->va) {
 					ax25_requeue_frames(ax25);
 				}
+				dama_check_need_response(ax25, type, pf);	/* dl1bke 960114 */
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
@@ -618,18 +716,33 @@
 			}
 			ax25_frames_acked(ax25, nr);
 			if (ax25->condition & OWN_RX_BUSY_CONDITION) {
-				if (pf) ax25_enquiry_response(ax25);
+				if (pf)
+				{	/* dl1bke 960114 */
+					if (ax25->dama_slave)
+						ax25_enquiry_response(ax25);
+					else
+						dama_enquiry_response(ax25);
+				}
 				break;
 			}
 			if (ns == ax25->vr) {
 				queued = ax25_rx_iframe(ax25, skb);
 				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
-					if (pf) ax25_enquiry_response(ax25);
+					if (pf) 
+					{	/* dl1bke 960114 */
+						if (ax25->dama_slave)
+							dama_enquiry_response(ax25);
+						else
+							ax25_enquiry_response(ax25);
+					}
 					break;
 				}
 				ax25->vr = (ax25->vr + 1) % ax25->modulus;
 				ax25->condition &= ~REJECT_CONDITION;
 				if (pf) {
+					if (ax25->dama_slave) 	/* dl1bke 960114 */
+						dama_enquiry_response(ax25);
+					else
 					ax25_enquiry_response(ax25);
 				} else {
 					if (!(ax25->condition & ACK_PENDING_CONDITION)) {
@@ -639,9 +752,18 @@
 				}
 			} else {
 				if (ax25->condition & REJECT_CONDITION) {
-					if (pf) ax25_enquiry_response(ax25);
+					if (pf) 
+					{ 	/* dl1bke 960114 */
+						if (ax25->dama_slave)
+							dama_enquiry_response(ax25);
+						else
+							ax25_enquiry_response(ax25);
+					}
 				} else {
 					ax25->condition |= REJECT_CONDITION;
+					if (ax25->dama_slave)		/* dl1bke 960114 */
+						dama_enquiry_response(ax25);
+					else
 					ax25_send_control(ax25, REJ, pf, C_RESPONSE);
 					ax25->condition &= ~ACK_PENDING_CONDITION;
 				}
@@ -664,7 +786,7 @@
 /*
  *	Higher level upcall for a LAPB frame
  */
-int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type)
+int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
 {
 	int queued = 0, frametype, ns, nr, pf;
 	
@@ -683,16 +805,16 @@
 
 	switch (ax25->state) {
 		case AX25_STATE_1:
-			queued = ax25_state1_machine(ax25, skb, frametype, pf, type);
+			queued = ax25_state1_machine(ax25, skb, frametype, pf, type, dama);
 			break;
 		case AX25_STATE_2:
 			queued = ax25_state2_machine(ax25, skb, frametype, pf, type);
 			break;
 		case AX25_STATE_3:
-			queued = ax25_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
+			queued = ax25_state3_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
 			break;
 		case AX25_STATE_4:
-			queued = ax25_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
+			queued = ax25_state4_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
 			break;
 	}
 
diff -u --recursive --new-file v1.3.59/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v1.3.59/linux/net/ax25/ax25_out.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/ax25_out.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -24,6 +24,7 @@
  *			Jonathan(G4KLX)	Only poll when window is full.
  *	AX.25 030	Jonathan(G4KLX)	Added fragmentation to ax25_output.
  *					Added support for extended AX.25.
+ *			Joerg(DL1BKE)	Added DAMA support
  */
 
 #include <linux/config.h>
@@ -109,7 +110,10 @@
 	}
 
 	if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4)
-		ax25_kick(ax25);
+	{
+		if (!ax25->dama_slave)		/* bke 960114: we aren't allowed to transmit */
+			ax25_kick(ax25);	/* in DAMA mode unless we received a Poll */
+	}
 }
 
 /* 
@@ -184,8 +188,10 @@
 #endif
 			/*
 			 * Transmit the frame copy.
+			 * bke 960114: do not set the Poll bit on the last frame
+			 * in DAMA mode.
 			 */
-			ax25_send_iframe(ax25, skbn, (last) ? POLLON : POLLOFF);
+			ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? POLLON : POLLOFF);
 
 			ax25->vs = next;
 
@@ -312,10 +318,99 @@
 	}
 }
 
+/* dl1bke 960114: shouldn't ax25/dama_check_need_response reside as  */
+/*                static inline void ...() in ax25.h, should it? ;-) */
+
 void ax25_check_need_response(ax25_cb *ax25, int type, int pf)
 {
-	if (type == C_COMMAND && pf)
+	if (!ax25->dama_slave && type == C_COMMAND && pf)
 		ax25_enquiry_response(ax25);
+}
+
+/* dl1bke 960114: transmit I frames on DAMA poll */
+
+void dama_enquiry_response(ax25_cb * ax25)
+{
+	ax25_cb * ax25o = NULL;
+	
+	if (!(ax25->condition & PEER_RX_BUSY_CONDITION) )
+	{
+		ax25_requeue_frames(ax25);
+		ax25_kick(ax25);
+	}
+	if (ax25->state == AX25_STATE_1 ||
+	    ax25->state == AX25_STATE_2 ||
+	    skb_peek(&ax25->ack_queue) != NULL) 
+	{
+		ax25_t1_timeout(ax25);
+
+	} else
+		ax25->n2count = 0;
+	
+	ax25->t3timer = ax25->t3;
+	
+
+	/* The FLEXNET DAMA master implementation refuses to send us ANY */
+	/* I frame for this connection if we send a REJ here, probably   */
+	/* due to it's frame collector scheme? A simple RR or  RNR will  */
+	/* invoke the retransmission, and in fact REJs are superflous    */
+	/* in DAMA mode anyway...					 */
+	
+#if 0
+	if (ax25->condition & REJECT_CONDITION)
+		ax25_send_control(ax25, REJ, POLLOFF, C_RESPONSE);
+	else
+#endif	
+		ax25_enquiry_response(ax25);
+		
+	/* Note that above response to the poll could be sent behind the  */
+	/* transmissions of the other channels as well... This version    */	
+	/* gives better performance on FLEXNET nodes. (Why, Gunter?)	  */
+
+	for (ax25o=ax25_list; ax25o; ax25o=ax25o->next)
+	{
+		if (ax25o->device != ax25->device)
+			continue;
+			
+		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2)
+		{
+			ax25_t1_timeout(ax25o);
+			continue;
+		}
+			
+		if ( !ax25o->dama_slave)
+			continue;
+			
+		if ( !(ax25o->condition & PEER_RX_BUSY_CONDITION) && 
+		     (ax25o->state == AX25_STATE_3 || 
+		     (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0)) )
+		{
+			ax25_requeue_frames(ax25o);
+			ax25_kick(ax25o);
+		}
+		
+		if (ax25o->state == AX25_STATE_1 ||
+		    ax25o->state == AX25_STATE_2 ||
+		    skb_peek(&ax25o->ack_queue) != NULL) ax25_t1_timeout(ax25o);
+
+		ax25o->t3timer = ax25o->t3;
+	}
+}
+
+void dama_check_need_response(ax25_cb *ax25, int type, int pf)
+{
+	if (ax25->dama_slave && type == C_COMMAND && pf)
+		dama_enquiry_response(ax25);
+}
+
+void dama_establish_data_link(ax25_cb *ax25)
+{
+	ax25->condition = 0x00;
+	ax25->n2count   = 0;
+
+	ax25->t3timer = 0;
+	ax25->t2timer = 0;
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 }
 
 #endif
diff -u --recursive --new-file v1.3.59/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.59/linux/net/ax25/ax25_route.c	Mon Dec 11 15:42:10 1995
+++ linux/net/ax25/ax25_route.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -27,6 +27,10 @@
  *	AX.25 030	Jonathan(G4KLX)	Added digi-peaters to routing table, and
  *					ioctls to manipulate them. Added port
  *					configuration.
+ *	AX.25 031	Jonathan(G4KLX)	Added concept of default route.
+ *			Joerg(DL1BKE)	ax25_rt_build_path() find digipeater list and device by 
+ *					destination call. Needed for IP routing via digipeater
+ *
  */
  
 #include <linux/config.h>
@@ -44,6 +48,7 @@
 #include <net/ax25.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <asm/segment.h>
@@ -269,6 +274,7 @@
 	int len     = 0;
 	off_t pos   = 0;
 	off_t begin = 0;
+	char *callsign;
 	int i;
   
 	cli();
@@ -276,8 +282,12 @@
 	len += sprintf(buffer, "callsign  dev  count time      mode digipeaters\n");
 
 	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
+			callsign = "default";
+		else
+			callsign = ax2asc(&ax25_rt->callsign);
 		len += sprintf(buffer + len, "%-9s %-4s %5d %9d",
-			ax2asc(&ax25_rt->callsign),
+			callsign,
 			ax25_rt->dev ? ax25_rt->dev->name : "???",
 			ax25_rt->n,
 			ax25_rt->stamp.tv_sec);
@@ -359,42 +369,77 @@
 }
 
 /*
- *	Find what interface to use.
+ *	Find which interface to use.
  */
 int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
 {
+	struct ax25_route *ax25_spe_rt = NULL;
+	struct ax25_route *ax25_def_rt = NULL;
 	struct ax25_route *ax25_rt;
 	ax25_address *call;
-	
+
+	/*
+	 *	Bind to the physical interface we heard them on, or the default
+	 *	route if non is found;
+	 */
 	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-		if (ax25cmp(&ax25_rt->callsign, addr) == 0) {
-			/*
-			 *	Bind to the physical interface we heard them on.
-			 */
-			if ((ax25->device = ax25_rt->dev) == NULL)
-				continue;
-			if ((call = ax25_findbyuid(current->euid)) == NULL) {
-				if (ax25_uid_policy && !suser())
-					return -EPERM;
-				call = (ax25_address *)ax25->device->dev_addr;
-			}
-			memcpy(&ax25->source_addr, call, sizeof(ax25_address));
-			if (ax25_rt->digipeat != NULL) {
-				if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
-					return -ENOMEM;
-				memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
-			}
-			if (ax25->sk != NULL)
-				ax25->sk->zapped = 0;
+		if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
+			ax25_spe_rt = ax25_rt;
+		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
+			ax25_def_rt = ax25_rt;
+	}
 
-			return 0;			
-		}
+	if (ax25_spe_rt != NULL)
+		ax25_rt = ax25_spe_rt;
+	else if (ax25_def_rt != NULL)
+		ax25_rt = ax25_def_rt;
+	else
+		return -EHOSTUNREACH;
+
+	if ((call = ax25_findbyuid(current->euid)) == NULL) {
+		if (ax25_uid_policy && !suser())
+			return -EPERM;
+		call = (ax25_address *)ax25->device->dev_addr;
 	}
 
-	return -EINVAL;
+	memcpy(&ax25->source_addr, call, sizeof(ax25_address));
+
+	if (ax25_rt->digipeat != NULL) {
+		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+		memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
+	}
+
+	if (ax25->sk != NULL)
+		ax25->sk->zapped = 0;
+
+	return 0;
 }
 
 /*
+ *	dl1bke 960117: build digipeater path
+ */
+void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr)
+{
+	struct ax25_route *ax25_rt;
+
+	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+		if ( ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->digipeat != NULL ) {
+		
+			if (ax25_rt->dev == NULL)
+				continue;
+			
+			if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
+				return;
+
+			ax25->device = ax25_rt->dev;
+			memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
+			return;
+		}
+	}
+	ax25->digipeat = NULL;
+}
+/*
  *	Register the mode of an incoming IP frame. It is assumed that an entry
  *	already exists in the routing table.
  */
@@ -518,6 +563,7 @@
 	struct device *dev;
 	struct ax25_dev *ax25_dev;
 	int err;
+	int count;
 
 	switch (cmd) {
 		case SIOCAX25SETPARMS:
@@ -662,6 +708,8 @@
 				return err;
 			memcpy_fromfs(&bpqaddr, arg, sizeof(bpqaddr));
 			if ((dev = dev_get(bpqaddr.dev)) == NULL)
+				return -EINVAL;
+			if (dev->type != ARPHRD_ETHER)
 				return -EINVAL;
 			for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
 				if (bpqdev->dev == dev) {
diff -u --recursive --new-file v1.3.59/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.59/linux/net/ax25/ax25_subr.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/ax25_subr.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -25,6 +25,8 @@
  *					Added fragmentation support.
  *			Darryl(G7LED)	Added function ax25_requeue_frames() to split
  *					it up from ax25_frames_acked().
+ *			Joerg(DL1BKE)	DAMA needs KISS Fullduplex ON/OFF.
+ *					Thus we have ax25_kiss_cmd() now... ;-)
  */
 
 #include <linux/config.h>
@@ -94,6 +96,8 @@
 			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
 			ax25->va = (ax25->va + 1) % ax25->modulus;
+			if (ax25->dama_slave)		/* dl1bke 960120 */
+				ax25->n2count = 0;
 		}
 	}
 }
@@ -324,7 +328,7 @@
  *	Given an AX.25 address pull of to, from, digi list, command/response and the start of data
  *
  */
-unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags)
+unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
 {
 	int d = 0;
 	
@@ -341,6 +345,8 @@
 		}
 	}
 		
+	if (dama != NULL) *dama = ~(buf[13] & DAMA_FLAG);
+		
 	/* Copy to, from */
 	if (dest != NULL) memcpy(dest, buf + 0, AX25_ADDR_LEN);
 	if (src != NULL)  memcpy(src,  buf + 7, AX25_ADDR_LEN);
@@ -460,6 +466,61 @@
 
 	/* Finish off */
 	out->lastrepeat = 0;
+}
+
+void ax25_kiss_cmd(ax25_cb * ax25, unsigned char cmd, unsigned char param)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	
+	if (ax25->device == NULL)
+		return;
+
+	if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
+		return;
+		
+	skb->free = 1;
+	skb->arp = 1;
+	
+	if (ax25->sk != NULL)
+	{
+		skb->sk = ax25->sk;
+		ax25->sk->wmem_alloc += skb->truesize;
+	}
+	
+	skb->protocol = htons(ETH_P_AX25);
+	
+	p = skb_put(skb, 2);
+	
+	*p++=cmd;
+	*p  =param;
+	
+	dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL);	
+}
+
+void ax25_dama_on(ax25_cb *ax25)
+{
+	int count = ax25_dev_is_dama_slave(ax25->device);
+
+	if (count == 0)
+	{
+		if (ax25->sk && ax25->sk->debug)
+			printk("DAMA on\n");
+		ax25_kiss_cmd(ax25, 5, 1);
+	}
+}
+
+void ax25_dama_off(ax25_cb *ax25)
+{
+	int count = ax25_dev_is_dama_slave(ax25->device);
+	
+	
+	if (count == 0)
+	{
+		if (ax25->sk && ax25->sk->debug)
+			printk("DAMA off\n");
+		ax25_kiss_cmd(ax25, 5, 0);
+	}
 }
 
 #endif
diff -u --recursive --new-file v1.3.59/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v1.3.59/linux/net/ax25/ax25_timer.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/ax25_timer.c	Wed Feb  7 08:55:44 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 030
+ *	AX.25 release 031
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -111,6 +111,7 @@
 			if (ax25->sk != NULL) {
 				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & OWN_RX_BUSY_CONDITION)) {
 					ax25->condition &= ~OWN_RX_BUSY_CONDITION;
+					if (!ax25->dama_slave) /* dl1bke */
 					ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE);
 					ax25->condition &= ~ACK_PENDING_CONDITION;
 					break;
@@ -119,7 +120,8 @@
 			/*
 			 * Check for frames to transmit.
 			 */
-			ax25_kick(ax25);
+			if (!ax25->dama_slave)
+				ax25_kick(ax25);	/* dl1bke 960114 */
 			break;
 
 		default:
@@ -130,12 +132,41 @@
 		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
 			if (ax25->condition & ACK_PENDING_CONDITION) {
 				ax25->condition &= ~ACK_PENDING_CONDITION;
+				if (!ax25->dama_slave)			/* dl1bke 960114 */
 				ax25_timeout_response(ax25);
 			}
 		}
 	}
 
-	if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
+	if (ax25->t3timer > 0 && --ax25->t3timer == 0) 
+	{
+	
+		/* dl1bke 960114: T3 expires and we are in DAMA mode:  */
+		/*                send a DISC and abort the connection */
+		 
+		if (ax25->dama_slave)
+		{
+#ifdef CONFIG_NETROM
+			nr_link_failed(&ax25->dest_addr, ax25->device);
+#endif
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+				
+			ax25->state = AX25_STATE_0;
+			if (ax25->sk != NULL)
+			{
+				if (ax25->sk->debug)
+					printk("T3 Timeout\n");
+				ax25->sk->state = TCP_CLOSE;
+				ax25->sk->err   = ETIMEDOUT;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead  = 1;
+			}
+			
+			return;
+		}
+		
 		if (ax25->state == AX25_STATE_3) {
 			ax25->n2count = 0;
 			ax25_transmit_enquiry(ax25);
@@ -144,11 +175,35 @@
 		ax25->t3timer = ax25->t3;
 	}
 
+	/* dl1bke 960114: DAMA T1 timeouts are handled in ax25_dama_slave_transmit */
+	/* 		  nevertheless we have to re-enqueue the timer struct...   */
+	
 	if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
+	
 		ax25_reset_timer(ax25);
 		return;
 	}
 
+	if (!ax25_dev_is_dama_slave(ax25->device))
+	{
+		if (ax25->dama_slave) ax25->dama_slave = 0;
+		ax25_t1_timeout(ax25);
+	}
+}
+
+
+/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
+ *                within the poll of any connected channel. Remember 
+ *                that we are not allowed to send anything unless we
+ *                get polled by the Master.
+ *                
+ *                Thus we'll have to do parts of our T1 handling in
+ *                ax25_enquiry_response().
+ */
+
+
+void ax25_t1_timeout(ax25_cb * ax25)
+{	
 	switch (ax25->state) {
 		case AX25_STATE_1: 
 			if (ax25->n2count == ax25->n2) {
@@ -169,14 +224,14 @@
 					ax25->modulus = MODULUS;
 					ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
 					ax25->n2count = 0;
-					ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
+					ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
 				}
 			} else {
 				ax25->n2count++;
 				if (ax25->modulus == MODULUS) {
-					ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
+					ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
 				} else {
-					ax25_send_control(ax25, SABME, POLLON, C_COMMAND);
+					ax25_send_control(ax25, SABME, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
 				}
 			}
 			break;
@@ -188,6 +243,8 @@
 #endif
 				ax25_clear_queues(ax25);
 				ax25->state = AX25_STATE_0;
+				ax25_send_control(ax25, DISC, POLLON, C_COMMAND); /* dl1bke */
+				
 				if (ax25->sk != NULL) {
 					ax25->sk->state = TCP_CLOSE;
 					ax25->sk->err   = ETIMEDOUT;
@@ -197,12 +254,14 @@
 				}
 			} else {
 				ax25->n2count++;
+				if (!ax25_dev_is_dama_slave(ax25->device))	/* dl1bke */
 				ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
 			}
 			break;
 
 		case AX25_STATE_3: 
 			ax25->n2count = 1;
+			if (!ax25->dama_slave)			/* dl1bke 960114 */
 			ax25_transmit_enquiry(ax25);
 			ax25->state   = AX25_STATE_4;
 			break;
@@ -216,6 +275,8 @@
 				ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
 				ax25->state = AX25_STATE_0;
 				if (ax25->sk != NULL) {
+					if (ax25->sk->debug)
+						printk("Link Failure\n");
 					ax25->sk->state = TCP_CLOSE;
 					ax25->sk->err   = ETIMEDOUT;
 					if (!ax25->sk->dead)
@@ -224,6 +285,7 @@
 				}
 			} else {
 				ax25->n2count++;
+				if (!ax25->dama_slave)		/* dl1bke 960114 */
 				ax25_transmit_enquiry(ax25);
 			}
 			break;
diff -u --recursive --new-file v1.3.59/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.59/linux/net/core/dev.c	Sun Jan 14 16:30:16 1996
+++ linux/net/core/dev.c	Wed Feb  7 08:55:44 1996
@@ -41,7 +41,8 @@
  *		Rudi Cilibrasi	:	Pass the right thing to set_mac_address()
  *		Dave Miller	:	32bit quantity for the device lock to make it work out
  *					on a Sparc.
- *		Bjorn Ekwall	:	Added KERNELD hack
+ *		Bjorn Ekwall	:	Added KERNELD hack.
+ *		Alan Cox	:	Cleaned up the backlog initialise.
  *
  */
 
@@ -69,6 +70,7 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/arp.h>
+#include <net/slhc.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #ifdef CONFIG_NET_ALIAS
@@ -103,13 +105,7 @@
  *	queue in the bottom half handler.
  */
 
-static struct sk_buff_head backlog = 
-{
-	(struct sk_buff *)&backlog, (struct sk_buff *)&backlog
-#if CONFIG_SKB_CHECK
-	,SK_HEAD_SKB
-#endif
-};
+static struct sk_buff_head backlog;
 
 /* 
  *	We don't overdo the queue or we will thrash memory badly.
@@ -207,17 +203,23 @@
 		if (strcmp(dev->name, name) == 0)
 			return(dev);
 	}
-#ifdef CONFIG_KERNELD
-	if (request_module(name) == 0)
-		for (dev = dev_base; dev != NULL; dev = dev->next) {
-			if (strcmp(dev->name, name) == 0)
-				return(dev);
-		}
-#endif
-	return(NULL);
+	return NULL;
 }
+	
+/*
+ *	Find and possibly load an interface.
+ */
+ 
+#ifdef CONFIG_KERNELD
 
+extern __inline__ void dev_load(const char *name)
+{
+	if(!dev_get(name))
+		request_module(name);
+}
 
+#endif
+ 
 /*
  *	Prepare an interface for use. 
  */
@@ -316,8 +318,6 @@
 	return notifier_chain_unregister(&netdev_chain,nb);
 }
 
-
-
 /*
  *	Send (or queue for sending) a packet. 
  *
@@ -388,6 +388,15 @@
 	if (!where)	 	/* Always keep order. It helps other hosts
 					   far more than it costs us */
 	{
+		/*
+		 *	Check we are not overruning the device queue length.
+		 */
+		 
+		if(skb_queue_len(dev->buffs + pri) > dev->tx_queue_len)
+		{
+			dev_kfree_skb(skb, FREE_WRITE);
+			return;
+		}
 		skb_queue_tail(dev->buffs + pri,skb);
 		skb_device_unlock(skb);		/* Buffer is on the device queue and can be freed safely */
 		skb = skb_dequeue(dev->buffs + pri);
@@ -451,6 +460,7 @@
 	 *	when freed. These will be updated later as the frames get
 	 *	owners.
 	 */
+
 	skb->sk = NULL;
 	skb->free = 1;
 	if(skb->stamp.tv_sec==0)
@@ -493,93 +503,6 @@
 	return;
 }
 
-
-/*
- *	The old interface to fetch a packet from a device driver.
- *	This function is the base level entry point for all drivers that
- *	want to send a packet to the upper (protocol) levels.  It takes
- *	care of de-multiplexing the packet to the various modules based
- *	on their protocol ID.
- *
- *	Return values:	1 <- exit I can't do any more
- *			0 <- feed me more (i.e. "done", "OK"). 
- *
- *	This function is OBSOLETE and should not be used by any new
- *	device.
- */
-
-int dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
-{
-	static int dropping = 0;
-	struct sk_buff *skb = NULL;
-	unsigned char *to;
-	int amount, left;
-	int len2;
-
-	if (dev == NULL || buff == NULL || len <= 0) 
-		return(1);
-
-	if (flags & IN_SKBUFF) 
-	{
-		skb = (struct sk_buff *) buff;
-	}
-	else
-	{
-		if (dropping) 
-		{
-			if (skb_peek(&backlog) != NULL)
-				return(1);
-			printk("INET: dev_rint: no longer dropping packets.\n");
-			dropping = 0;
-		}
-
-		skb = alloc_skb(len, GFP_ATOMIC);
-		if (skb == NULL) 
-		{
-			printk("dev_rint: packet dropped on %s (no memory) !\n",
-			       dev->name);
-			dropping = 1;
-			return(1);
-		}
-
-		/* 
-		 *	First we copy the packet into a buffer, and save it for later. We
-		 *	in effect handle the incoming data as if it were from a circular buffer
-		 */
-
-		to = skb_put(skb,len);
-		left = len;
-
-		len2 = len;
-		while (len2 > 0) 
-		{
-			amount = min(len2, (unsigned long) dev->rmem_end -
-						(unsigned long) buff);
-			memcpy(to, buff, amount);
-			len2 -= amount;
-			left -= amount;
-			buff += amount;
-			to += amount;
-			if ((unsigned long) buff == dev->rmem_end)
-				buff = (unsigned char *) dev->rmem_start;
-		}
-	}
-
-	/*
-	 *	Tag the frame and kick it to the proper receive routine
-	 */
-	 
-	skb->dev = dev;
-	skb->free = 1;
-
-	netif_rx(skb);
-	/*
-	 *	OK, all done. 
-	 */
-	return(0);
-}
-
-
 /*
  *	This routine causes all interfaces to try to send some data. 
  */
@@ -781,7 +704,7 @@
 	unsigned long flags;
 	
 	/*
-	 * aliases do not trasmit (by now :)
+	 * aliases do not transmit (for now :) )
 	 */
 
 #ifdef CONFIG_NET_ALIAS
@@ -909,6 +832,7 @@
  *	in detail.
  */
 
+#ifdef CONFIG_PROC_FS
 static int sprintf_stats(char *buffer, struct device *dev)
 {
 	struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
@@ -975,6 +899,7 @@
 		len=length;		/* Ending slop */
 	return len;
 }
+#endif	/* CONFIG_PROC_FS */
 
 
 /*
@@ -1024,12 +949,16 @@
 	 *	only allow alias creation/deletion if (getset==SIOCSIFADDR)
 	 *
 	 */
+	 
+#ifdef CONFIG_KERNELD
+	dev_load(ifr.ifr_name);
+#endif	
 
 #ifdef CONFIG_NET_ALIAS
 	if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
 		return(err);
 #else
-	if ((dev = dev_get(ifr.ifr_name)) == NULL) 
+	if ((dev = dev_get(ifr.ifr_name)) == NULL) 	
 		return(-ENODEV);
 #endif
 	switch(getset) 
@@ -1388,6 +1317,12 @@
 	struct device *dev, **dp;
 
 	/*
+	 *	Initialise the packet receive queue.
+	 */
+	 
+	skb_queue_head_init(&backlog);
+	
+	/*
 	 * This is VeryUgly(tm).
 	 *
 	 * Some devices want to be initialized eary..
@@ -1404,6 +1339,13 @@
 #if defined(CONFIG_DEC_ELCP)
 	dec21040_init();
 #endif	
+	/*
+	 *	SLHC if present needs attaching so other people see it
+	 *	even if not opened.
+	 */
+#if defined(CONFIG_SLIP_COMPRESSED) || defined(CONFIG_PPP)
+	slhc_install();
+#endif	
 
 	/*
 	 *	Add the devices.
@@ -1433,12 +1375,14 @@
 		}
 	}
 
+#ifdef CONFIG_PROC_FS
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_DEV, 3, "dev",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 		0, &proc_net_inode_operations,
 		dev_get_info
 	});
+#endif
 
 	/*	
 	 *	Initialise net_alias engine 
diff -u --recursive --new-file v1.3.59/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.59/linux/net/core/skbuff.c	Sat Nov 25 19:04:57 1995
+++ linux/net/core/skbuff.c	Wed Feb  7 08:55:44 1996
@@ -10,6 +10,8 @@
  *	Richard Kooijman	:	Timestamp fixes.
  *		Alan Cox	:	Changed buffer format.
  *		Alan Cox	:	destructor hook for AF_UNIX etc.
+ *		Linus Torvalds	:	Better skb_clone.
+ *		Alan Cox	:	Added skb_copy.
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -190,6 +192,7 @@
 {
 	list->prev = (struct sk_buff *)list;
 	list->next = (struct sk_buff *)list;
+	list->qlen = 0;
 	list->magic_debug_cookie = SK_HEAD_SKB;
 }
 
@@ -215,6 +218,8 @@
 
 	newsk->next->prev = newsk;
 	newsk->prev->next = newsk;
+	newsk->list = list_;
+	list_->qlen++;
 
 	restore_flags(flags);
 }
@@ -240,6 +245,9 @@
 
 	newsk->next->prev = newsk;
 	newsk->prev->next = newsk;
+	
+	newsk->list = list_;
+	list_->qlen++;
 
 	restore_flags(flags);
 }
@@ -271,7 +279,9 @@
 
 	result->next = NULL;
 	result->prev = NULL;
-
+	list_->qlen--;
+	result->list = NULL;
+	
 	restore_flags(flags);
 
 	IS_SKB(result);
@@ -299,6 +309,8 @@
 	newsk->prev = old->prev;
 	old->prev = newsk;
 	newsk->prev->next = newsk;
+	newsk->list = old->list;
+	newsk->list->qlen++;
 
 	restore_flags(flags);
 }
@@ -325,6 +337,8 @@
 	newsk->next = old->next;
 	newsk->next->prev = newsk;
 	old->next = newsk;
+	newsk->list = old->list;
+	newsk->list->qlen++;
 
 	restore_flags(flags);
 }
@@ -344,12 +358,14 @@
 
 	IS_SKB(skb);
 
-	if(skb->prev && skb->next)
+	if(skb->list)
 	{
+		skb->list->qlen--;
 		skb->next->prev = skb->prev;
 		skb->prev->next = skb->next;
 		skb->next = NULL;
 		skb->prev = NULL;
+		skb->list = NULL;
 	}
 #ifdef PARANOID_BUGHUNT_MODE	/* This is legal but we sometimes want to watch it */
 	else
@@ -458,7 +474,7 @@
   	if (skb->free == 2)
 		printk("Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
 			__builtin_return_address(0));
-	if (skb->next)
+	if (skb->list)
 	 	printk("Warning: kfree_skb passed an skb still on a list (from %p).\n",
 			__builtin_return_address(0));
 
@@ -549,8 +565,8 @@
 	skb->free = 2;	/* Invalid so we pick up forgetful users */
 	skb->lock = 0;
 	skb->pkt_type = PACKET_HOST;	/* Default type */
-	skb->prev = skb->next = NULL;
-	skb->link3 = NULL;
+	skb->prev = skb->next = skb->link3 = NULL;
+	skb->list = NULL;
 	skb->sk = NULL;
 	skb->truesize=size;
 	skb->localroute=0;
@@ -604,7 +620,7 @@
  *	Duplicate an sk_buff. The new one is not owned by a socket or locked
  *	and will be freed on deletion.
  */
-#if 1
+
 struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
 {
 	struct sk_buff *n;
@@ -626,17 +642,37 @@
 	restore_flags(flags);
 	n->data_skb = skb;
 	n->next = n->prev = n->link3 = NULL;
+	n->list = NULL;
 	n->sk = NULL;
 	n->truesize = sizeof(*n);
 	n->free = 1;
 	n->tries = 0;
 	n->lock = 0;
 	n->users = 0;
+	n->h.raw=skb->h.raw;
+	n->mac.raw=skb->mac.raw;
+	n->dev=skb->dev;
+	n->ip_hdr=skb->ip_hdr;
+	n->saddr=skb->saddr;
+	n->daddr=skb->daddr;
+	n->raddr=skb->raddr;
+	n->seq=skb->seq;
+	n->end_seq=skb->end_seq;
+	n->ack_seq=skb->ack_seq;
+	n->acked=skb->acked;
+	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+	n->used=skb->used;
+	n->arp=skb->arp;
+	n->pkt_type=skb->pkt_type;
+	n->stamp=skb->stamp;
 	return n;
 }
-#else
-/* this is slower, and copies the whole data area */
-struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
+
+/*
+ *	This is slower, and copies the whole data area 
+ */
+ 
+struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
 {
 	struct sk_buff *n;
 	unsigned long offset;
@@ -664,6 +700,7 @@
 	/* Copy the bytes */
 	memcpy(n->head,skb->head,skb->end-skb->head);
 	n->link3=NULL;
+	n->list=NULL;
 	n->sk=NULL;
 	n->when=skb->when;
 	n->dev=skb->dev;
@@ -673,9 +710,9 @@
 	n->saddr=skb->saddr;
 	n->daddr=skb->daddr;
 	n->raddr=skb->raddr;
-	n->raddr=skb->seq;
-	n->raddr=skb->end_seq;
-	n->raddr=skb->ack_seq;
+	n->seq=skb->seq;
+	n->end_seq=skb->end_seq;
+	n->ack_seq=skb->ack_seq;
 	n->acked=skb->acked;
 	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
 	n->used=skb->used;
@@ -690,7 +727,6 @@
 	IS_SKB(n);
 	return n;
 }
-#endif
 
 /*
  *     Skbuff device locking
diff -u --recursive --new-file v1.3.59/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.59/linux/net/core/sock.c	Wed Dec 27 09:12:14 1995
+++ linux/net/core/sock.c	Wed Feb  7 08:55:44 1996
@@ -67,6 +67,7 @@
  *		Alan Cox	:	Added optimistic memory grabbing for AF_UNIX throughput.
  *		Alan Cox	:	Allocator for a socket is settable.
  *		Alan Cox	:	SO_ERROR includes soft errors.
+ *		Alan Cox	:	Allow NULL arguments on some SO_ opts
  *
  * To Fix:
  *
@@ -125,6 +126,19 @@
 	int err;
 	struct linger ling;
 
+	/*
+	 *	Options without arguments
+	 */
+
+#ifdef SO_DONTLINGER		/* Compatibility item... */
+	switch(optname)
+	{
+		case SO_DONTLINGER:
+			sk->linger=0;
+			return 0;
+	}
+#endif	
+		
   	if (optval == NULL) 
   		return(-EINVAL);
 
@@ -508,7 +522,7 @@
 			if (sk->wmem_alloc + size >= sk->sndbuf) 
 #endif
 			{
-				if (sk->wmem_alloc <= 0)
+				if (sk->wmem_alloc < 0)
 				  printk("sock.c: Look where I am %ld<%ld\n", tmp, sk->wmem_alloc);
 				sk->socket->flags &= ~SO_NOSPACE;
 				interruptible_sleep_on(sk->sleep);
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.59/linux/net/ipv4/af_inet.c	Wed Dec 27 09:12:14 1995
+++ linux/net/ipv4/af_inet.c	Wed Feb  7 08:55:44 1996
@@ -46,6 +46,8 @@
  *		Germano Caronni	:	Assorted small races.
  *		Alan Cox	:	sendmsg/recvmsg basic support.
  *		Alan Cox	:	Only sendmsg/recvmsg now supported.
+ *		Alan Cox	:	Locked down bind (see security list).
+ *		Alan Cox	:	Loosened bind a little.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -665,7 +667,8 @@
 
 	/* this is how many unacked bytes we will accept for this socket.  */
 	sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
-
+	sk->delay_acks = 1;
+	sk->max_ack_backlog = SOMAXCONN;
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->receive_queue);
 	sk->mtu = 576;
@@ -872,7 +875,7 @@
 		 *      used by get_sock_*(), and saddr is used for transmit.
 		 *
 		 *      In the BSD API these are the same except where it
-		 *      would be illegal to use (multicast/broadcast) in
+		 *      would be illegal to use them (multicast/broadcast) in
 		 *      which case the sending device address is used.
 		 */
 		sk->rcv_saddr = addr->sin_addr.s_addr;
@@ -888,19 +891,49 @@
 		for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
 					sk2 != NULL; sk2 = sk2->next) 
 		{
-		/* should be below! */
+			/*
+			 *	Hash collision or real match ?
+			 */
+			 
 			if (sk2->num != snum) 
 				continue;
+				
+			/*
+			 *	Either bind on the port is wildcard means
+			 *	they will overlap and thus be in error
+			 */			
+			 
+			if (!sk2->rcv_saddr || !sk->rcv_saddr)
+			{
+				/*
+				 *	Allow only if both are setting reuse.
+				 */
+				if(sk2->reuse && sk->reuse)
+					continue;
+				sti();
+				return(-EADDRINUSE);
+			}
+
+			/*
+			 *	Two binds match ?
+			 */
+
+			if (sk2->rcv_saddr != sk->rcv_saddr) 
+				continue;
+			/*
+			 *	Reusable port ?
+			 */
+
 			if (!sk->reuse)
 			{
 				sti();
 				return(-EADDRINUSE);
 			}
 			
-			if (sk2->num != snum) 
-				continue;		/* more than one */
-			if (sk2->rcv_saddr != sk->rcv_saddr) 
-				continue;	/* socket per slot ! -FB */
+			/*
+			 *	Reuse ?
+			 */
+			 
 			if (!sk2->reuse || sk2->state==TCP_LISTEN) 
 			{
 				sti();
@@ -1540,10 +1573,16 @@
 #if defined(CONFIG_IP_ALIAS)
 	ip_alias_init();
 #endif
+
+#ifdef CONFIG_INET_RARP
+	rarp_ioctl_hook = rarp_ioctl;
+#endif
 	/*
 	 *	Create all the /proc entries.
 	 */
 
+#ifdef CONFIG_PROC_FS
+
 #ifdef CONFIG_INET_RARP
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_RARP, 4, "rarp",
@@ -1551,8 +1590,8 @@
 		0, &proc_net_inode_operations,
 		rarp_get_info
 	});
-	rarp_ioctl_hook = rarp_ioctl;
-#endif
+#endif		/* RARP */
+
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_RAW, 3, "raw",
 		S_IFREG | S_IRUGO, 1, 0, 0,
@@ -1595,4 +1634,5 @@
 		0, &proc_net_inode_operations,
 		rt_cache_get_info
 	});
+#endif		/* CONFIG_PROC_FS */
 }
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.59/linux/net/ipv4/arp.c	Wed Dec 27 09:12:14 1995
+++ linux/net/ipv4/arp.c	Wed Feb  7 08:55:44 1996
@@ -1660,7 +1660,7 @@
 	u32 ip;
 
 	/*
-	 *	Find out about the hardware type.
+	 *	Extract destination.
 	 */
 	
 	si = (struct sockaddr_in *) &r->arp_pa;
@@ -1680,8 +1680,16 @@
 		ip_rt_put(rt);
 	}
 
-	if (!dev)	/* this is can only be NULL if ATF_PUBL is not set */
+	/* good guess about the device if it isn't a ATF_PUBL entry */
+	if (!dev) {
+		if (dev1->flags&(IFF_LOOPBACK|IFF_NOARP))
+			return -ENODEV;
 		dev = dev1;
+	}
+
+	/* this needs to be checked only for dev=dev1 but it doesnt hurt */	
+	if (r->arp_ha.sa_family != dev->type)	
+		return -EINVAL;
 		
 	if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
 	    (!(r->arp_flags & ATF_PUBL) && dev != dev1))
@@ -1997,11 +2005,13 @@
 	/* Register for device down reports */
 	register_netdevice_notifier(&arp_dev_notifier);
 
+#ifdef CONFIG_PROC_FS
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_ARP, 3, "arp",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 		0, &proc_net_inode_operations,
 		arp_get_info
 	});
+#endif
 }
 
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.59/linux/net/ipv4/igmp.c	Wed Dec 27 09:12:14 1995
+++ linux/net/ipv4/igmp.c	Wed Feb  7 08:55:44 1996
@@ -46,6 +46,11 @@
  *		Tsu-Sheng Tsao		if the specified time expired.
  *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted.
  *		Alan Cox	:	Use GFP_ATOMIC in the right places.
+ *		Christian Daudt :	igmp timer wasn't set for local group
+ *					memberships but was being deleted, 
+ *					which caused a "del_timer() called 
+ *					from %p with timer not initialized\n"
+ *					message (960131).
  */
 
 
@@ -188,8 +193,13 @@
 
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
-	del_timer(&im->timer);
-	im->tm_running=0;
+  if (im->tm_running) {
+    del_timer(&im->timer);
+    im->tm_running=0;
+  }
+  else {
+    printk("igmp_stop_timer() called with timer not running by %p\n",__builtin_return_address(0));
+  }
 }
 
 extern __inline__ int random(void)
@@ -271,9 +281,15 @@
 static void igmp_heard_report(struct device *dev, unsigned long address)
 {
 	struct ip_mc_list *im;
-	for(im=dev->ip_mc_list;im!=NULL;im=im->next)
-		if(im->multiaddr==address)
-			igmp_stop_timer(im);
+
+	if ((address & IGMP_LOCAL_GROUP_MASK) != IGMP_LOCAL_GROUP) {
+	  /* Timers are only set for non-local groups */
+	  for(im=dev->ip_mc_list;im!=NULL;im=im->next) {
+	    if(im->multiaddr==address) {
+	      igmp_stop_timer(im);
+	    }
+	  }
+	}
 }
 
 static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.59/linux/net/ipv4/ip_forward.c	Thu Jan  4 21:54:59 1996
+++ linux/net/ipv4/ip_forward.c	Wed Feb  7 08:55:46 1996
@@ -105,7 +105,7 @@
 	 */
 
 	
-	if(!(is_frag&4))
+	if(!(is_frag&IPFWD_MASQUERADED))
 	{
 		fw_res=call_fw_firewall(PF_INET, skb, skb->h.iph);
 		switch (fw_res) {
@@ -153,7 +153,7 @@
 	}
 
 #ifdef CONFIG_IP_MROUTE
-	if(!(is_frag&8))
+	if(!(is_frag&IPFWD_MULTICASTING))
 	{
 #endif	
 		/*
@@ -219,7 +219,7 @@
 		 */
 		dev2=skb->dev;
 		raddr=skb->raddr;
-		if(is_frag&16)		/* VIFF_TUNNEL mode */
+		if(is_frag&IPFWD_MULTITUNNEL)	/* VIFF_TUNNEL mode */
 			encap=20;
 		rt=NULL;
 	}
@@ -238,7 +238,7 @@
 		 * If this fragment needs masquerading, make it so...
 		 * (Dont masquerade de-masqueraded fragments)
 		 */
-		if (!(is_frag&4) && fw_res==FW_MASQUERADE)
+		if (!(is_frag&IPFWD_MASQUERADED) && fw_res==FW_MASQUERADE)
 			ip_fw_masquerade(&skb, dev2);
 #endif
 		IS_SKB(skb);
@@ -280,7 +280,7 @@
 			 */
 			skb2->protocol=htons(ETH_P_IP);
 #ifdef CONFIG_IP_MROUTE
-			if(is_frag&16)
+			if(is_frag&IPFWD_MULTITUNNEL)
 			{
 				skb_reserve(skb,(encap+dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
 				ip_encap(skb2,skb->len, dev2, raddr);
@@ -314,7 +314,7 @@
 			skb2 = skb;		
 			skb2->dev=dev2;
 #ifdef CONFIG_IP_MROUTE
-			if(is_frag&16)
+			if(is_frag&IPFWD_MULTITUNNEL)
 				ip_encap(skb,skb->len, dev2, raddr);
 			else
 			{
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.59/linux/net/ipv4/ip_fw.c	Fri Nov 17 08:42:33 1995
+++ linux/net/ipv4/ip_fw.c	Wed Feb  7 08:55:46 1996
@@ -24,6 +24,8 @@
  *		Wilfred Mollenvanger 7/7/1995.
  *	TCP attack protection.
  *		Alan Cox 25/8/95, based on information from bugtraq.
+ *	ICMP type printk, IP_FW_F_APPEND
+ *		Bernd Eckenfels 1996-01-31
  *
  * Masquerading functionality
  *
@@ -441,7 +443,7 @@
 				case IPPROTO_UDP:
 					printk("UDP ");
 				case IPPROTO_ICMP:
-					printk("ICMP ");
+					printk("ICMP:%d ",icmp_type);
 					break;
 				default:
 					printk("p=%d ",ip->protocol);
@@ -1082,6 +1084,10 @@
 		chtmp_prev=NULL;
 		for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next) 
 		{
+			if (ftmp->fw_flg & IP_FW_F_APPEND) {
+				chtmp_prev=chtmp;
+				continue;
+			}
 			addb4=0;
 			newkind=ftmp->fw_flg & IP_FW_F_KIND;
 			oldkind=chtmp->fw_flg & IP_FW_F_KIND;
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.59/linux/net/ipv4/ip_input.c	Sun Jan 14 16:30:16 1996
+++ linux/net/ipv4/ip_input.c	Wed Feb  7 08:55:46 1996
@@ -308,13 +308,13 @@
 	if(iph->frag_off)
 	{
 		if (iph->frag_off & htons(IP_MF))
-			is_frag|=1;
+			is_frag|=IPFWD_FRAGMENT;
 		/*
 		 *	Last fragment ?
 		 */
 	
 		if (iph->frag_off & htons(IP_OFFSET))
-			is_frag|=2;
+			is_frag|=IPFWD_LASTFRAG;
 	}
 	
 	/*
@@ -416,7 +416,7 @@
 		if (ip_fw_demasquerade(skb)) 
 		{
 			struct iphdr *iph=skb->h.iph;
-			if (ip_forward(skb, dev, is_frag|4, iph->daddr))
+			if (ip_forward(skb, dev, is_frag|IPFWD_MASQUERADED, iph->daddr))
 				kfree_skb(skb, FREE_WRITE);
 			return(0);
 		}
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.59/linux/net/ipv4/ip_output.c	Thu Jan  4 21:54:59 1996
+++ linux/net/ipv4/ip_output.c	Wed Feb  7 08:55:46 1996
@@ -1040,6 +1040,7 @@
 	struct sockaddr_in *s;
 	if(skb==NULL)
 		return;
+	skb->free=1;
 	nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo));
 	nrt->rtmsg_type=msg;
 	s=(struct sockaddr_in *)&nrt->rtmsg_dst;
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v1.3.59/linux/net/ipv4/ipmr.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/ipmr.c	Wed Feb  7 08:55:46 1996
@@ -13,6 +13,7 @@
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
  *	Alan Cox		:	Added the cache manager code
+ *	Alan Cox		:	Fixed the clone/copy bug and device race.
  *
  *	Status:
  *		Cache manager under test. Forwarding in vague test mode
@@ -704,14 +705,18 @@
 	__u32 raddr=skb->raddr;
 	if(vif->flags&VIFF_TUNNEL)
 	{
-		tunnel=16;
+		tunnel=IPFWD_MULTITUNNEL;
 		raddr=vif->remote;
 	}
 	vif->pkt_out++;
 	vif->bytes_out+=skb->len;
 	skb->dev=vif->dev;
 	skb->raddr=skb->h.iph->daddr;
-	if(ip_forward(skb, in_dev, frag|8|tunnel, raddr)==-1)
+	/*
+	 *	If the vif went down as we were forwarding.. just throw the
+	 *	frame.
+	 */
+	if(vif->dev==NULL || ip_forward(skb, in_dev, frag|IPFWD_MULTICASTING|tunnel, raddr)==-1)
 		kfree_skb(skb, FREE_WRITE);
 }
 
@@ -757,7 +762,11 @@
 		 	{
 		 		if(psend!=-1)
 		 		{
-		 			skb2=skb_clone(skb, GFP_ATOMIC);
+		 			/*
+		 			 *	May get variant mac headers
+		 			 *	so must copy -- boo hoo.
+		 			 */
+		 			skb2=skb_copy(skb, GFP_ATOMIC);
 		 			if(skb2)
 		 			{
 		 				skb2->free=1;
@@ -899,7 +908,7 @@
  
 void ip_mr_init(void)
 {
-	printk("Linux IP multicast router 0.04-might-work 8)\n");
+	printk("Linux IP multicast router 0.05-maybe-works 8)\n");
 	register_netdevice_notifier(&ip_mr_notifier);
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_IPMR_VIF, 9 ,"ip_mr_vif",
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.59/linux/net/ipv4/packet.c	Wed Nov  8 07:11:46 1995
+++ linux/net/ipv4/packet.c	Wed Feb  7 08:55:46 1996
@@ -436,7 +436,7 @@
 	if (saddr) 
 	{
 		saddr->sa_family = skb->dev->type;
-		memcpy(saddr->sa_data,skb->dev->name, 14);
+		strncpy(saddr->sa_data,skb->dev->name, 15);
 	}
 	
 	/*
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.59/linux/net/ipv4/raw.c	Wed Dec 27 09:12:14 1995
+++ linux/net/ipv4/raw.c	Wed Feb  7 08:55:52 1996
@@ -278,7 +278,7 @@
 			return -ENOBUFS;
 		memcpy_fromiovec(buf, msg->msg_iov, len);
 		fs=get_fs();
-		set_fs(get_fs());
+		set_fs(get_ds());
 		err=raw_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
 		set_fs(fs);
 		kfree_s(buf,len);
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.59/linux/net/ipv4/route.c	Sun Jan 14 16:30:17 1996
+++ linux/net/ipv4/route.c	Wed Feb  7 08:55:52 1996
@@ -39,7 +39,7 @@
  *					routing caches and better behaviour.
  *		
  *		Olaf Erb	:	irtt wasnt being copied right.
- *		Bjorn Ekwall	:	Added KERNELD hack for autodialling
+ *		Bjorn Ekwall	:	Kerneld route support.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -1498,11 +1498,21 @@
 
 	if (!f || (fi->fib_flags & RTF_REJECT))
 	{
+#ifdef CONFIG_KERNELD	
+		char wanted_route[20];
+#endif		
 #if RT_CACHE_DEBUG >= 2
 		printk("rt_route failed @%08x\n", daddr);
 #endif
 		ip_rt_unlock();
 		kfree_s(rth, sizeof(struct rtable));
+#ifdef CONFIG_KERNELD		
+		daddr=ntohl(daddr);
+		sprintf(wanted_route, "%d.%d.%d.%d",
+			(int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff,
+			(int)(daddr >> 8) & 0xff, (int)daddr & 0xff);
+		kerneld_route(wanted_route); 	/* Dynamic route request */
+#endif		
 		return NULL;
 	}
 
@@ -1517,7 +1527,7 @@
 			fi = f->fib_info;
 		}
 	}
-
+	
 	if (!f)
 	{
 		ip_rt_unlock();
@@ -1565,12 +1575,7 @@
 		ATOMIC_DECR(&rt->rt_refcnt);
 }
 
-#ifdef CONFIG_KERNELD
-static struct rtable * real_ip_rt_route
-#else
-struct rtable * ip_rt_route
-#endif
-	(__u32 daddr, int local)
+struct rtable * ip_rt_route(__u32 daddr, int local)
 {
 	struct rtable * rth;
 
@@ -1589,28 +1594,6 @@
 	}
 	return ip_rt_slow_route (daddr, local);
 }
-
-#ifdef CONFIG_KERNELD
-struct rtable * ip_rt_route(__u32 daddr, int local)
-{
-	struct rtable *rt;
-	char wanted_route[20];
-	long ipaddr = ntohl(daddr);
-
-	if ((rt = real_ip_rt_route(daddr, local)) == NULL) {
-		sprintf(wanted_route, "%d.%d.%d.%d",
-			(int)(ipaddr >> 24) & 0xff, (int)(ipaddr >> 16) & 0xff,
-			(int)(ipaddr >> 8) & 0xff, (int)ipaddr & 0xff);
-		/* Not good while forwarding a packet... see ipc/msg.c */
-		kerneld_route(wanted_route); /* perhaps a ppp-connection? */
-		/* Try again */
-		rt = real_ip_rt_route(daddr, local);
-	}
-
-	return rt;
-}
-#endif
-
 
 /*
  *	Process a route add request from the user, or from a kernel
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.59/linux/net/ipv4/tcp.c	Tue Jan 23 21:15:55 1996
+++ linux/net/ipv4/tcp.c	Wed Feb  7 08:55:52 1996
@@ -183,19 +183,22 @@
  *		Alan Cox	:	Fix MTU discovery pathalogical case
  *					when the remote claims no mtu!
  *		Marc Tamsky	:	TCP_CLOSE fix.
+ *		Colin (G3TNE)	:	Send a reset on syn ack replies in 
+ *					window but wrong (fixes NT lpd problems)
+ *		Pedro Roque	:	Better TCP window handling, delayed ack.
+ *		Joerg Reuter	:	No modification of locked buffers in 
+ *					tcp_do_retransmit()
  *
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
  *		so it doesn't iterate over the queue, also spot packets with no funny
  *		options arriving in order and process directly.
  *
- *		Rewrite output state machine to use a single queue and do low window
- *		situations as per the spec (RFC 1122)
+ *		Rewrite output state machine to use a single queue.
  *		Speed up input assembly algorithm.
  *		RFC1323 - PAWS and window scaling. PAWS is required for IPv6 so we
  *		could do with it working on IPv4
  *		User settable/learned rtt/max window/mtu
- *		Fix the window handling to use PR's new code.
  *
  *		Change the fundamental structure to a single send queue maintained
  *		by TCP (removing the bogus ip stuff [thus fixing mtu drops on
@@ -344,8 +347,8 @@
  *  SHOULD initialize RTO to 0 and RTT to 3. (does)
  * 
  * When to Send an ACK Segment (4.2.3.2)
- *   SHOULD implement delayed ACK. (does not)
- *   MUST keep ACK delay < 0.5 sec. (N/A)
+ *   SHOULD implement delayed ACK. (does)
+ *   MUST keep ACK delay < 0.5 sec. (does)
  * 
  * When to Send a Window Update (4.2.3.3)
  *   MUST implement receiver-side SWS. (does)
@@ -398,8 +401,8 @@
  *     address. (does) 
  * 
  * Asynchronous Reports (4.2.4.1)
- * **MUST provide mechanism for reporting soft errors to application
- *     layer. (doesn't)
+ * MUST provide mechanism for reporting soft errors to application
+ *     layer. (does)
  * 
  * Type of Service (4.2.4.2)
  *   MUST allow application layer to set Type of Service. (does IP_TOS)
@@ -468,10 +471,10 @@
 }
 
 static void tcp_close(struct sock *sk, int timeout);
-
+static void tcp_read_wakeup(struct sock *sk);
 
 /*
- *	The less said about this the better, but it works and will do for 1.2 
+ *	The less said about this the better, but it works and will do for 1.2  (and 1.4 ;))
  */
 
 static struct wait_queue *master_select_wakeup;
@@ -521,37 +524,91 @@
  *  
  *	1. The window can never be shrunk once it is offered (RFC 793)
  *	2. We limit memory per socket
- *   
- *	For now we use NET2E3's heuristic of offering half the memory
- *	we have handy. All is not as bad as this seems however because
- *	of two things. Firstly we will bin packets even within the window
- *	in order to get the data we are waiting for into the memory limit.
- *	Secondly we bin common duplicate forms at receive time
- *	Better heuristics welcome
  */
-   
-int tcp_select_window(struct sock *sk)
+
+
+static __inline__ unsigned short tcp_select_window(struct sock *sk)
 {
-	int new_window = sock_rspace(sk);
+	long free_space = sock_rspace(sk);	
+	long window = 0;
+
+	if (free_space > 1024)
+		free_space &= ~0x3FF;  /* make free space a multiple of 1024 */
+ 
+	if(sk->window_clamp)
+		free_space = min(sk->window_clamp, free_space);
+ 
+	/* 
+         * compute the actual window i.e. 
+         * old_window - received_bytes_on_that_win 
+	 */
+
+	if (sk->mss == 0)
+		sk->mss = sk->mtu;
+
+	window = sk->window - (sk->acked_seq - sk->lastwin_seq);
+ 
+	if ( window < 0 ) {	
+		window = 0;
+		printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n", 
+		       sk->window, sk->acked_seq, sk->lastwin_seq);
+	}
+
+        /*
+	 * RFC 1122:
+	 * "the suggested [SWS] avoidance algoritm for the receiver is to keep
+	 *  RECV.NEXT + RCV.WIN fixed until:
+	 *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
+	 * 
+	 * i.e. don't raise the right edge of the window until you can't raise
+	 * it MSS bytes
+	 */
 	
+	if ( (free_space - window) >= min(sk->mss, MAX_WINDOW/2) )
+		window += ((free_space - window) / sk->mss) * sk->mss;
+	
+	sk->window = window;
+	sk->lastwin_seq = sk->acked_seq;
+	
+	return sk->window;
+}
+
+/*
+ *      This function returns the amount that we can raise the
+ *      usable window.
+ */
+
+static __inline__ unsigned short tcp_raise_window(struct sock *sk)
+{
+	long free_space = sock_rspace(sk);
+	long window = 0;
+
+	if (free_space > 1024)
+		free_space &= ~0x3FF; /* make free space a multiple of 1024 */
+
 	if(sk->window_clamp)
-		new_window=min(sk->window_clamp,new_window);
-	/*
-	 * 	Two things are going on here.  First, we don't ever offer a
-	 * 	window less than min(sk->mss, MAX_WINDOW/2).  This is the
-	 * 	receiver side of SWS as specified in RFC1122.
-	 * 	Second, we always give them at least the window they
-	 * 	had before, in order to avoid retracting window.  This
-	 * 	is technically allowed, but RFC1122 advises against it and
-	 * 	in practice it causes trouble.
-	 *
-	 * 	Fixme: This doesn't correctly handle the case where
-	 *	new_window > sk->window but not by enough to allow for the
-	 *	shift in sequence space. 
-	 */
-	if (new_window < min(sk->mss, MAX_WINDOW/2) || new_window < sk->window)
-		return(sk->window);
-	return(new_window);
+		free_space = min(sk->window_clamp, free_space);
+ 
+	/* 
+         * compute the actual window i.e. 
+         * old_window - received_bytes_on_that_win 
+	 */
+
+	window = sk->window - (sk->acked_seq - sk->lastwin_seq);
+
+	if (sk->mss == 0)
+		sk->mss = sk->mtu;
+ 
+	if ( window < 0 ) {	
+		window = 0;
+		printk(KERN_DEBUG "TRW: win < 0 w=%d 1=%u 2=%u\n", 
+		       sk->window, sk->acked_seq, sk->lastwin_seq);
+	}
+	
+	if ( (free_space - window) >= min(sk->mss, MAX_WINDOW/2) )
+		return ((free_space - window) / sk->mss) * sk->mss;
+
+	return 0;
 }
 
 /*
@@ -649,6 +706,19 @@
 		dev = skb->dev;
 		IS_SKB(skb);
 		skb->when = jiffies;
+		
+		/* dl1bke 960201 - @%$$! Hope this cures strange race conditions    */
+		/*		   with AX.25 mode VC. (esp. DAMA)		    */
+		/*		   if the buffer is locked we should not retransmit */
+		/*		   anyway, so we don't need all the fuss to prepare */
+		/*		   the buffer in this case. 			    */
+		/*		   (the skb_pull() changes skb->data while we may   */
+		/*		   actually try to send the data. Ough. A side	    */
+		/*		   effect is that we'll send some unnecessary data, */
+		/*		   but the alternative is desastrous...		    */
+		
+		if (skb_device_locked(skb))
+			break;
 
 		/*
 		 *	Discard the surplus MAC header
@@ -735,6 +805,8 @@
 			 */
 		 
 			th->ack_seq = htonl(sk->acked_seq);
+			sk->ack_backlog = 0;
+			sk->bytes_rcv = 0;
 			th->window = ntohs(tcp_select_window(sk));
 			tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
 		
@@ -768,7 +840,6 @@
 		 */
 		 
 		ct++;
-		sk->retransmits++;
 		sk->prot->retransmits ++;
 		tcp_statistics.TcpRetransSegs++;
 		
@@ -977,14 +1048,9 @@
 	sk->inuse = 1;
 	sti();
 
-	/* Always see if we need to send an ack. */
 
-	if (sk->ack_backlog) 
-	{
-		sk->prot->read_wakeup (sk);
-		if (! sk->dead)
-			sk->data_ready(sk,0);
-	}
+	if (sk->ack_backlog && !sk->dead) 
+		sk->data_ready(sk,0);
 
 	/* Now we need to figure out why the socket was on the timer. */
 
@@ -1009,6 +1075,8 @@
 			skb = sk->send_head;
 			if (!skb) 
 			{
+				if (sk->ack_backlog)
+					tcp_read_wakeup(sk);
 				restore_flags(flags);
 			} 
 			else 
@@ -1019,6 +1087,8 @@
 				 */
 				if (jiffies < skb->when + sk->rto) 
 				{
+					if (sk->ack_backlog)
+						tcp_read_wakeup(sk);
 					reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
 					restore_flags(flags);
 					break;
@@ -1364,8 +1434,6 @@
 	return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
 }
 
-
-
 void tcp_send_check(struct tcphdr *th, unsigned long saddr, 
 		unsigned long daddr, int len, struct sock *sk)
 {
@@ -1481,6 +1549,10 @@
 		 
 		sk->prot->queue_xmit(sk, skb->dev, skb, 0);
 		
+		
+		sk->ack_backlog = 0;
+		sk->bytes_rcv = 0;
+
 		/*
 		 *	Set for next retransmit based on expected ACK time.
 		 *	FIXME: We set this every time which means our 
@@ -1559,6 +1631,7 @@
 }
 
 
+
 /*
  *	This routine sends an ack and also updates the window. 
  */
@@ -1645,22 +1718,19 @@
 	 *	to a keepalive.
 	 */
 	 
-	if (ack == sk->acked_seq) 
-	{
+	if (ack == sk->acked_seq) {	       	  
 		sk->ack_backlog = 0;
 		sk->bytes_rcv = 0;
 		sk->ack_timed = 0;
+
 		if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL
-				  && sk->ip_xmit_timeout == TIME_WRITE) 
-		{
-			if(sk->keepopen) {
-				reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
-			} else {
-				delete_timer(sk);
-			}
-		}
-  	}
-  	
+		    && sk->ip_xmit_timeout == TIME_WRITE) 	
+		  if(sk->keepopen) 
+		    reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
+		  else 
+		    delete_timer(sk);	         		
+	}
+
   	/*
   	 *	Fill in the packet and send it
   	 */
@@ -2069,6 +2139,7 @@
 /*
  *	Send an ack if one is backlogged at this point. Ought to merge
  *	this with tcp_send_ack().
+ *      This is called for delayed acks also.
  */
  
 static void tcp_read_wakeup(struct sock *sk)
@@ -2134,8 +2205,11 @@
 	t1->urg = 0;
 	t1->syn = 0;
 	t1->psh = 0;
+
+
 	sk->ack_backlog = 0;
 	sk->bytes_rcv = 0;
+
 	sk->window = tcp_select_window(sk);
 	t1->window = htons(sk->window);
 	t1->ack_seq = htonl(sk->acked_seq);
@@ -2899,6 +2973,7 @@
 	sk->mss = min(sk->max_window >> 1, sk->mtu);
 #else    
 	sk->mss = min(sk->max_window, sk->mtu);
+	sk->max_unacked = 2 * sk->mss;
 #endif  
 }
 
@@ -3043,6 +3118,8 @@
 	newsk->shutdown = 0;
 	newsk->ack_backlog = 0;
 	newsk->acked_seq = skb->seq+1;
+	newsk->lastwin_seq = skb->seq+1;
+	newsk->delay_acks = 1;
 	newsk->copied_seq = skb->seq+1;
 	newsk->fin_seq = skb->seq;
 	newsk->state = TCP_SYN_RECV;
@@ -3197,9 +3274,8 @@
 	t1->source = newsk->dummy_th.source;
 	t1->seq = ntohl(buff->seq);
 	t1->ack = 1;
-	newsk->window = tcp_select_window(newsk);
 	newsk->sent_seq = newsk->write_seq;
-	t1->window = ntohs(newsk->window);
+	t1->window = ntohs(tcp_select_window(newsk));
 	t1->res1 = 0;
 	t1->res2 = 0;
 	t1->rst = 0;
@@ -3388,6 +3464,10 @@
 			 
 			sk->prot->queue_xmit(sk, skb->dev, skb, skb->free);
 			
+			
+			sk->ack_backlog = 0;
+			sk->bytes_rcv = 0;
+
 			/*
 			 *	Again we slide the timer wrongly
 			 */
@@ -4277,16 +4357,10 @@
 	{
 		if (before(skb->seq, sk->acked_seq+1)) 
 		{
-			int newwindow;
 
 			if (after(skb->end_seq, sk->acked_seq)) 
-			{
-				newwindow = sk->window - (skb->end_seq - sk->acked_seq);
-				if (newwindow < 0)
-					newwindow = 0;	
-				sk->window = newwindow;
 				sk->acked_seq = skb->end_seq;
-			}
+
 			skb->acked = 1;
 
 			/*
@@ -4306,14 +4380,8 @@
 				if (before(skb2->seq, sk->acked_seq+1)) 
 				{
 					if (after(skb2->end_seq, sk->acked_seq))
-					{
-						newwindow = sk->window -
-						 (skb2->end_seq - sk->acked_seq);
-						if (newwindow < 0)
-							newwindow = 0;	
-						sk->window = newwindow;
 						sk->acked_seq = skb2->end_seq;
-					}
+
 					skb2->acked = 1;
 					/*
 					 * 	When we ack the fin, we do
@@ -4339,18 +4407,26 @@
 			/*
 			 *	This also takes care of updating the window.
 			 *	This if statement needs to be simplified.
+			 *
+			 *      rules for delaying an ack:
+			 *      - delay time <= 0.5 HZ
+			 *      - we don't have a window update to send
+			 *      - must send at least every 2 full sized packets
 			 */
 			if (!sk->delay_acks ||
 			    sk->ack_backlog >= sk->max_ack_backlog || 
-			    sk->bytes_rcv > sk->max_unacked || th->fin) {
+			    sk->bytes_rcv > sk->max_unacked || th->fin ||
+			    sk->ato > HZ/2 ||
+			    tcp_raise_window(sk)) {
 	/*			tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr); */
 			}
 			else 
 			{
 				sk->ack_backlog++;
-				if(sk->debug)
+				
+				if(sk->debug)				
 					printk("Ack queued.\n");
-				reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME);
+				reset_xmit_timer(sk, TIME_WRITE, sk->ato);
 			}
 		}
 	}
@@ -4393,7 +4469,7 @@
 		}
 		tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
 		sk->ack_backlog++;
-		reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME);
+		reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, 0.5 * HZ));
 	}
 	else
 	{
@@ -4823,7 +4899,7 @@
 	struct tcphdr *th;
 	struct sock *sk;
 	int syn_ok=0;
-	
+
 	tcp_statistics.TcpInSegs++;
 	if(skb->pkt_type!=PACKET_HOST)
 	{
@@ -5041,6 +5117,9 @@
 				{
 					/* A valid ack from a different connection
 					   start. Shouldn't happen but cover it */
+                 			tcp_statistics.TcpAttemptFails++;
+                                        tcp_reset(daddr, saddr, th,
+                                                sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
 					kfree_skb(skb, FREE_READ);
 					release_sock(sk);
 					return 0;
@@ -5051,6 +5130,7 @@
 				 */
 				syn_ok=1;	/* Don't reset this connection for the syn */
 				sk->acked_seq = skb->seq+1;
+				sk->lastwin_seq = skb->seq+1;
 				sk->fin_seq = skb->seq;
 				tcp_send_ack(sk->sent_seq,sk->acked_seq,sk,th,sk->daddr);
 				tcp_set_state(sk, TCP_ESTABLISHED);
@@ -5163,6 +5243,43 @@
 		return tcp_std_reset(sk,skb);	
 	}
 
+
+	/*
+	 *	Delayed ACK time estimator.
+	 */
+	
+	if (sk->lrcvtime == 0) 
+	{
+		sk->lrcvtime = jiffies;
+		sk->ato = HZ/3;
+	}
+	else 
+	{
+		int m;
+		
+		m = jiffies - sk->lrcvtime;
+
+		sk->lrcvtime = jiffies;
+
+		if (m <= 0)
+			m = 1;
+
+		if (m > (sk->rtt >> 3)) 
+		{
+			sk->ato = sk->rtt >> 3;
+			/*
+			 * printk(KERN_DEBUG "ato: rtt %lu\n", sk->ato);
+			 */
+		}
+		else 
+		{
+			sk->ato = (sk->ato >> 1) + m;
+			/*
+			 * printk(KERN_DEBUG "ato: m %lu\n", sk->ato);
+			 */
+		}
+	}
+	  
 	/*
 	 *	Process the ACK
 	 */
diff -u --recursive --new-file v1.3.59/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.59/linux/net/ipv4/udp.c	Sun Jan 14 16:30:20 1996
+++ linux/net/ipv4/udp.c	Wed Feb  7 08:55:56 1996
@@ -429,7 +429,7 @@
 			return -ENOBUFS;
 		memcpy_fromiovec(buf, msg->msg_iov, len);
 		fs=get_fs();
-		set_fs(get_fs());
+		set_fs(get_ds());
 		err=udp_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
 		set_fs(fs);
 		kfree_s(buf,len);
@@ -474,7 +474,7 @@
 				 * of this packet since that is all
 				 * that will be read.
 				 */
-				amount = skb->len;
+				amount = skb->len-sizeof(struct udphdr);
 			}
 			err=verify_area(VERIFY_WRITE,(void *)arg,
 						sizeof(unsigned long));
diff -u --recursive --new-file v1.3.59/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.59/linux/net/ipx/af_ipx.c	Thu Jan  4 21:54:59 1996
+++ linux/net/ipx/af_ipx.c	Wed Feb  7 08:55:56 1996
@@ -258,6 +258,27 @@
 	return s;
 }
 
+#ifdef CONFIG_IPX_INTERN
+
+static ipx_socket *
+ipxitf_find_internal_socket(ipx_interface *intrfc,
+			    unsigned char *node, unsigned short port)
+{
+	ipx_socket *s = intrfc->if_sklist;
+
+	while (s != NULL)
+	{
+		if (   (s->ipx_port == port)
+		    && (memcmp(node, s->ipx_node, IPX_NODE_LEN) == 0))
+		{
+			break;
+		}
+		s = s->next;
+	}
+	return s;
+}	
+#endif
+
 static void ipxrtr_del_routes(ipx_interface *);
 
 static void
@@ -331,12 +352,8 @@
 	if((retval = sock_queue_rcv_skb(sock, skb))<0) 
 	{
 		/*
-	 	 *	We do a FREE_WRITE here because this indicates how
-	 	 *	to treat the socket with which the packet is 
-	 	 *	associated.  If this packet is associated with a
-	 	 *	socket at all, it must be the originator of the 
-	 	 *	packet.   Incoming packets will have no socket 
-		 *	associated with them at this point.
+		 * skb->sk is NULL here, so FREE_WRITE does not hurt
+		 * the sending socket.
 	 	 */
 		kfree_skb(skb,FREE_WRITE);
 	}
@@ -344,12 +361,76 @@
 }
 
 /*
- *	On input if skb->sk is set the buffer is a socket sending. We need to ensure the
- *	accounting is kept right in these cases. At the moment the socket write queue is
- *	charged for the memory.
+ * On input skb->sk is NULL. Nobody is charged for the memory.
  */
- 
-static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
+
+#ifdef CONFIG_IPX_INTERN
+static int
+ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
+{
+	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
+	ipx_socket	*s;
+
+	int is_broadcast = (memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
+				   IPX_NODE_LEN) == 0);
+
+	s = intrfc->if_sklist;
+
+	while (s != NULL)
+	{
+		if (   (s->ipx_port == ipx->ipx_dest.sock)
+		    && (   is_broadcast
+			|| (memcmp(ipx->ipx_dest.node, s->ipx_node,
+				   IPX_NODE_LEN) == 0)))
+		{
+			/* We found a socket to which to send */
+			struct sk_buff *skb1;
+
+			if (copy != 0)
+			{
+				skb1 = skb_clone(skb, GFP_ATOMIC);
+				if (skb1 != NULL)
+				{
+					skb1->arp = skb1->free = 1;
+				}
+				else
+				{
+					return -ENOMEM;
+				}
+			}
+			else
+			{
+				skb1 = skb;
+				copy = 1; /* skb may only be used once */
+			}
+			ipxitf_def_skb_handler(s, skb1);
+
+			if (intrfc != ipx_internal_net)
+			{
+				/* on an external interface, at most
+                                 * one socket can listen.
+				 */
+				break;
+			}
+		}
+		s = s->next;
+	}
+
+	if (copy == 0)
+	{
+		/* skb was solely for us, and we did not make a copy,
+		 * so free it. FREE_WRITE does not hurt, because
+		 * skb->sk is NULL here.
+		 */
+		kfree_skb(skb, FREE_WRITE);
+	}
+	return 0;
+}
+
+#else
+
+static int
+ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
 {
 	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
 	ipx_socket	*sock1 = NULL, *sock2 = NULL;
@@ -444,6 +525,7 @@
 
 	return 0;
 }
+#endif
 
 static struct sk_buff *
 ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
@@ -508,7 +590,10 @@
 			 *	Don't charge sender
 			 */
 			if(skb->sk)
+			{
 				skb->sk->wmem_alloc-=skb->truesize;
+				skb->sk=NULL;
+			}
 			/*
 			 *	Will charge receiver
 			 */
@@ -520,7 +605,10 @@
 		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
 		{
 			if (!send_to_wire && skb->sk)
+			{
 				skb->sk->wmem_alloc-=skb->truesize;
+				skb->sk=NULL;
+			}
 			ipxitf_demux_socket(intrfc, skb, send_to_wire);
 			if (!send_to_wire) 
 				return 0;
@@ -995,6 +1083,8 @@
 		rt->ir_next=ipx_routes;
 		ipx_routes=rt;
 	}
+	else if (intrfc == ipx_internal_net)
+		return(-EINVAL);
 
 	rt->ir_net = network;
 	rt->ir_intrfc = intrfc;
@@ -1110,7 +1200,21 @@
 	skb->h.raw = (unsigned char *)ipx;
 
 	ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
-	memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+#ifdef CONFIG_IPX_INTERN
+	memcpy(ipx->ipx_source.node, sk->ipx_node, IPX_NODE_LEN);
+#else
+	if ((err = ntohs(sk->ipx_port)) == 0x453 || err == 0x452)  
+	{
+		/* RIP/SAP special handling for mars_nwe */
+		ipx->ipx_source.net = intrfc->if_netnum;
+		memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN);
+	}
+	else
+	{
+		ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
+		memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+	}
+#endif
 	ipx->ipx_source.sock = sk->ipx_port;
 	ipx->ipx_dest.net=usipx->sipx_network;
 	memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
@@ -1221,7 +1325,7 @@
 	off_t begin=0;
 
 	/* Theory.. Keep printing in the same place until we pass offset */
-	
+
 	len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network", 
 		"Node_Address", "Primary", "Device", "Frame_Type");
 	for (i = ipx_interfaces; i != NULL; i = i->if_next) {
@@ -1265,15 +1369,29 @@
 	off_t begin=0;
 
 	/* Theory.. Keep printing in the same place until we pass offset */
-	
+
+#ifdef CONFIG_IPX_INTERN	
+	len += sprintf (buffer,"%-28s%-28s%-10s%-10s%-7s%s\n", "Local_Address", 
+#else
 	len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address", 
+#endif
 			"Remote_Address", "Tx_Queue", "Rx_Queue", 
 			"State", "Uid");
 	for (i = ipx_interfaces; i != NULL; i = i->if_next) {
 		for (s = i->if_sklist; s != NULL; s = s->next) {
-			len += sprintf (buffer+len,"%08lX:%04X  ", 
-				htonl(i->if_netnum),
-				htons(s->ipx_port));
+#ifdef CONFIG_IPX_INTERN
+			len += sprintf(buffer+len,
+				       "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
+				       htonl(s->ipx_intrfc->if_netnum),
+				       s->ipx_node[0], s->ipx_node[1], 
+				       s->ipx_node[2], s->ipx_node[3], 
+				       s->ipx_node[4], s->ipx_node[5],
+				       htons(s->ipx_port));
+#else
+			len += sprintf(buffer+len,"%08lX:%04X  ", 
+				       htonl(i->if_netnum),
+				       htons(s->ipx_port));
+#endif
 			if (s->state!=TCP_ESTABLISHED) {
 				len += sprintf(buffer+len, "%-28s", "Not_Connected");
 			} else {
@@ -1436,6 +1554,7 @@
 		return err;
 	put_fs_long(sizeof(int),(unsigned long *)optlen);
 	err=verify_area(VERIFY_WRITE,optval,sizeof(int));
+	if (err) return err;
 	put_fs_long(val,(unsigned long *)optval);
 	return(0);
 }
@@ -1577,7 +1696,59 @@
 
 	if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
 		return -EPERM;	/* protect IPX system stuff like routing/sap */
-	
+
+	sk->ipx_port=addr->sipx_port;
+
+#ifdef CONFIG_IPX_INTERN
+	if (intrfc == ipx_internal_net)
+	{
+		/* The source address is to be set explicitly if the
+		 * socket is to be bound on the internal network. If a
+		 * node number 0 was specified, the default is used.
+		 */
+
+		if (memcmp(addr->sipx_node, ipx_broadcast_node,
+			   IPX_NODE_LEN) == 0)
+		{
+			return -EINVAL;
+		}
+		if (memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN) == 0)
+		{
+			memcpy(sk->ipx_node, intrfc->if_node,
+			       IPX_NODE_LEN);
+		}
+		else
+		{
+			memcpy(sk->ipx_node, addr->sipx_node, IPX_NODE_LEN);
+		}
+		if (ipxitf_find_internal_socket(intrfc, sk->ipx_node,
+						sk->ipx_port) != NULL)
+		{
+			if(sk->debug)
+				printk("IPX: bind failed because port %X in"
+				       " use.\n", (int)addr->sipx_port);
+			return -EADDRINUSE;
+		}
+	}
+	else
+	{
+		/* Source addresses are easy. It must be our
+		 * network:node pair for an interface routed to IPX
+		 * with the ipx routing ioctl()
+		 */
+
+		memcpy(sk->ipx_node, intrfc->if_node, IPX_NODE_LEN);
+		
+		if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
+			if(sk->debug)
+				printk("IPX: bind failed because port %X in"
+				       " use.\n", (int)addr->sipx_port);
+			return -EADDRINUSE;	   
+		}
+	}
+
+#else
+
 	/* Source addresses are easy. It must be our network:node pair for
 	   an interface routed to IPX with the ipx routing ioctl() */
 
@@ -1588,7 +1759,8 @@
 		return -EADDRINUSE;	   
 	}
 
-	sk->ipx_port=addr->sipx_port;
+#endif
+
 	ipxitf_insert_socket(intrfc, sk);
 	sk->zapped=0;
 	if(sk->debug)
@@ -1616,8 +1788,13 @@
 		int ret;
 	
 		uaddr.sipx_port = 0;
-		uaddr.sipx_network = 0L; 
-		ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+		uaddr.sipx_network = 0L;
+#ifdef CONFIG_IPX_INTERN
+		memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
+		       IPX_NODE_LEN);
+#endif
+		ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
+				sizeof(struct sockaddr_ipx));
 		if (ret != 0) return (ret);
 	}
 	
@@ -1665,8 +1842,13 @@
 	} else {
 		if (sk->ipx_intrfc != NULL) {
 			sipx.sipx_network = sk->ipx_intrfc->if_netnum;
+#ifdef CONFIG_IPX_INTERN
+			memcpy(sipx.sipx_node, sk->ipx_node, IPX_NODE_LEN);
+#else
 			memcpy(sipx.sipx_node, sk->ipx_intrfc->if_node,
-				IPX_NODE_LEN);
+			       IPX_NODE_LEN);
+#endif
+
 		} else {
 			sipx.sipx_network = 0L;
 			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
@@ -1797,7 +1979,12 @@
 
 			uaddr.sipx_port = 0;
 			uaddr.sipx_network = 0L; 
-			ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+#ifdef CONFIG_IPX_INTERN
+			memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
+			       IPX_NODE_LEN);
+#endif
+			ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
+					sizeof(struct sockaddr_ipx));
 			if (ret != 0) return ret;
 		}
 
@@ -1901,7 +2088,7 @@
 			struct sk_buff *skb;
 			/* These two are safe on a single CPU system as only user tasks fiddle here */
 			if((skb=skb_peek(&sk->receive_queue))!=NULL)
-				amount=skb->len;
+				amount=skb->len-sizeof(struct ipx_packet);
 			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
 			if(err)
 				return err;
diff -u --recursive --new-file v1.3.59/linux/net/netlink.c linux/net/netlink.c
--- v1.3.59/linux/net/netlink.c	Sun Dec 17 11:43:33 1995
+++ linux/net/netlink.c	Wed Feb  7 08:55:56 1996
@@ -72,6 +72,7 @@
 	unsigned int minor = MINOR(inode->i_rdev);
 	struct sk_buff *skb;
 	skb=alloc_skb(count, GFP_KERNEL);
+	skb->free=1;
 	memcpy_fromfs(skb_put(skb,count),buf, count);
 	return (netlink_handler[minor])(skb);
 }
diff -u --recursive --new-file v1.3.59/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.59/linux/net/netrom/af_netrom.c	Mon Dec 11 15:42:11 1995
+++ linux/net/netrom/af_netrom.c	Wed Feb  7 08:55:56 1996
@@ -60,12 +60,6 @@
 #include <net/arp.h>
 #include <linux/if_arp.h>
 
-/************************************************************************\
-*									*
-*			Handlers for the socket list			*
-*									*
-\************************************************************************/
-
 struct nr_parms_struct nr_default;
 
 static unsigned short circuit = 0x101;
@@ -278,19 +272,14 @@
 	restore_flags(flags);
 }
 
-/*******************************************************************************************************************\
-*													            *
-* Handling for system calls applied via the various interfaces to a NET/ROM socket object		    	    *
-*														    *
-\*******************************************************************************************************************/
+/*
+ *	Handling for system calls applied via the various interfaces to a
+ *	NET/ROM socket object.
+ */
  
 static int nr_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	switch(cmd)
-	{
-		default:
-			return(-EINVAL);
-	}
+	return -EINVAL;
 }
 
 static int nr_setsockopt(struct socket *sock, int level, int optname,
@@ -686,14 +675,6 @@
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
-#ifdef DONTDO
-	if (nr_find_listener(&addr->fsa_ax25.sax25_call, sk->type) != NULL) {
-		if (sk->debug)
-			printk("NET/ROM: bind failed: in use\n");
-		return -EADDRINUSE;
-	}
-#endif
-
 	if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
 		if (sk->debug)
 			printk("NET/ROM: bind failed: invalid node callsign\n");
@@ -759,12 +740,12 @@
 	if (addr_len != sizeof(struct sockaddr_ax25))
 		return -EINVAL;
 
-	if ((dev = nr_dev_first()) == NULL)
-		return -ENETUNREACH;
-		
 	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
 		sk->zapped = 0;
 
+		if ((dev = nr_dev_first()) == NULL)
+			return -ENETUNREACH;
+
 		source = (ax25_address *)dev->dev_addr;
 
 		if ((user = ax25_findbyuid(current->euid)) == NULL) {
@@ -852,8 +833,10 @@
 	if (sk->state != TCP_LISTEN)
 		return -EINVAL;
 		
-	/* The write queue this time is holding sockets ready to use
-	   hooked into the SABM we saved */
+	/*
+	 *	The write queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
 	do {
 		cli();
 		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
diff -u --recursive --new-file v1.3.59/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.59/linux/net/unix/af_unix.c	Wed Dec 27 09:12:15 1995
+++ linux/net/unix/af_unix.c	Wed Feb  7 08:55:56 1996
@@ -38,6 +38,7 @@
  *	socketpair(...SOCK_RAW..) doesnt panic the kernel.
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/signal.h>
@@ -971,6 +972,7 @@
 	return(0);
 }
 
+#ifdef CONFIG_PROC_FS
 static int unix_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	off_t pos=0;
@@ -1010,6 +1012,7 @@
 		len=length;
 	return len;
 }
+#endif
 
 static struct proto_ops unix_proto_ops = {
 	AF_UNIX,
@@ -1038,12 +1041,14 @@
 {
 	printk("NET3: Unix domain sockets 0.10 BETA for Linux NET3.033.\n");
 	sock_register(unix_proto_ops.family, &unix_proto_ops);
+#ifdef CONFIG_PROC_FS
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_UNIX,  4, "unix",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 		0, &proc_net_inode_operations,
 		unix_get_info
 	});
+#endif
 }
 /*
  * Local variables:
diff -u --recursive --new-file v1.3.59/linux/scripts/Configure linux/scripts/Configure
--- v1.3.59/linux/scripts/Configure	Fri Jan 26 01:37:08 1996
+++ linux/scripts/Configure	Wed Feb  7 10:09:44 1996
@@ -168,7 +168,7 @@
 #	tristate question define
 #
 function tristate () {
-        if [ "$MODULES" != "y" ]; then
+        if [ "$CONFIG_MODULES" != "y" ]; then
           bool "$1" "$2"
         else 
 	  def=$(eval echo "\${$2:-'n'}")
diff -u --recursive --new-file v1.3.59/linux/scripts/Makefile linux/scripts/Makefile
--- v1.3.59/linux/scripts/Makefile	Thu Nov  9 11:23:56 1995
+++ linux/scripts/Makefile	Wed Feb  7 09:30:38 1996
@@ -1,4 +1,4 @@
-HOSTCFLAGS=-g
+# HOSTCFLAGS:=$(HOSTCFLAGS) -g
 HEADER=header.tk
 TAIL=tail.tk
 
@@ -12,7 +12,7 @@
 	cat ${HEADER} >> ./kconfig.tk
 	cat kconfig.tmp >> kconfig.tk
 	rm -f kconfig.tmp
-	echo "set defaults \"arch/${ARCH}/config.in\"" >> kconfig.tk
+	echo "set defaults \"arch/${ARCH}/defconfig\"" >> kconfig.tk
 	cat ${TAIL} >> kconfig.tk
 	chmod 755 kconfig.tk
 
diff -u --recursive --new-file v1.3.59/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v1.3.59/linux/scripts/Menuconfig	Thu Jan  1 02:00:00 1970
+++ linux/scripts/Menuconfig	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,901 @@
+#! /bin/sh
+#
+# This script is used to configure the linux kernel.
+#
+# It was inspired by a desire to not have to hit <enter> 9 million times
+# or startup the X server just to change a single kernel parameter.  
+#
+# This script attempts to parses the configuration files, which are
+# scattered throughout the kernel source tree, and create a temporary
+# set of mini scripts which are in turn used to create nested menus and
+# radiolists.
+#
+# It uses a very modified/mutilated version of the "dialog" utility
+# written by Savio Lam (lam836@cs.cuhk.hk). Savio is not responsible
+# for this script or the version of dialog used by this script.
+# Please do not contact him with questions. The official version of 
+# dialog is available at sunsite.unc.edu or a sunsite mirror.
+#
+# Portions of this script were borrowed from the original Configure
+# script used in linux-1.3.55.
+#
+# Please send comments / questions / bug fixes to roadcapw@cfw.com
+#
+#----------------------------------------------------------------------------
+
+#
+# Make sure we're really running bash.
+#
+[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
+
+#
+# Cache function definitions
+#
+set -h
+
+#
+# Extract available help for an option from Configure.help
+# and send it to standard output.
+#
+# Most of this function was borrowed from the original kernel
+# Configure script.
+#
+function extract_help () {
+  if [ -f Documentation/Configure.help ]
+  then
+     #first escape regexp special characters in the argument:
+     var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+     #now pick out the right help text:
+     text=$(sed -n "/^$var[ 	]*\$/,\${
+                        /^$var[ 	]*\$/b
+                        /^#.*/b;/^[ 	]*\$/q
+                        p
+                    }" Documentation/Configure.help)
+
+     if [ -z "$text" ]
+     then
+	  echo "There is no help available for this setting."
+     else
+	  echo "$text"
+     fi
+  else
+	 echo "There is no help available for this setting."
+  fi
+}
+
+#
+# Activate a help dialog.
+#
+function help () {
+	extract_help $2 >help.out
+	$DIALOG --backtitle "$backtitle" --title "$1" --textbox help.out 20 75
+	rm help.out
+}
+
+
+#
+# Init temporary scripts for the current menu.
+#
+function menu_name () {
+	echo -ne "$DIALOG --title '$1'\
+			--backtitle '$backtitle' \
+			--menu '$menu_instructions' \
+			21 75 11 '$default' " >submenu
+	>radiolists
+}
+
+#
+# Additional comments are currently semi-supported
+#
+function comment () {
+	if [ "$comments_ok" ]
+	then
+		$DIALOG --backtitle "$backtitle" --msgbox "$*" 7 70
+		comments_ok=
+	fi
+}
+
+#
+# Don't need this yet, but we don't want to puke either.
+#
+function define_bool () {
+	:	
+}
+
+#
+# Add a submenu option to the menu currently under construction.
+#
+function submenu () {
+	echo -ne "'activate_menu $2' '$1  --->' " >>submenu
+}
+
+#
+# This is to handle the Sound configuration.
+#
+function yuck1 () {
+	echo -ne "'clear ; $MAKE $2' '$1' " >>submenu
+}
+
+#
+# Create a boolean (Yes/No) function for our current menu
+# which calls our local bool function.
+#
+function bool () {
+	eval $2=\${$2:-'n'}  x=\$$2
+
+	case $x in
+	y|m) yes='ON' no='OFF' flag="*"
+	   ;;
+	n) yes='OFF' no='ON' flag=" "
+	   ;;
+	esac
+
+	echo -ne "'$2' '($flag) $1' " >>submenu
+
+	echo -e "function $2 () { l_bool '$1' '$yes' '$no' '$2' }\n" \
+		>>radiolists
+}
+
+#
+# Handle a boolean (Yes/No) option.
+#
+function l_bool () {
+	while true
+	do
+		if $DIALOG --title "$1" \
+			--backtitle "$backtitle" \
+			--radiolist "$radiolist_instructions" 12 70 2 \
+			'y' 'Yes' $2 'n' 'No' $3 2>dialog.out
+		then
+			eval $4=`cat dialog.out`
+			break
+		fi
+			
+		help "$1" "$4"
+	done
+	comments_ok=TRUE
+}
+
+#
+# Same as bool() except options are (Module/No)
+#
+function mod_bool () {
+	eval $2=\${$2:-'n'}  x=\$$2
+
+	case $x in
+	m) module='ON'  no='OFF' flag='M'
+	   ;;
+	*) module='OFF' no='ON'  flag=' '
+	   ;;
+	esac
+
+	echo -ne "'$2' '($flag) $1' " >>submenu
+
+	echo -e "function $2 () { l_mod_bool '$1' '$module' '$no' '$2' }\n" \
+		>>radiolists
+}
+
+#
+# Same as l_bool() except options are (Module/No)
+#
+function l_mod_bool() {
+	while true
+	do
+		if $DIALOG --title "$1" \
+			--backtitle "$backtitle" \
+			--radiolist "$radiolist_instructions" 12 70 2 \
+			'm' 'Module' $2 'n' 'No' $3 2>dialog.out
+		then
+			eval $4=`cat dialog.out`
+			break
+		fi
+			
+		help "$1" "$4"
+	done
+	comments_ok=TRUE
+}
+
+#
+# Create a tristate (Yes/No/Module) radiolist function
+# which calls our local tristate function.
+#
+# Collapses to a boolean (Yes/No) if module support is disabled.
+#
+function tristate () {
+	if [ "$CONFIG_MODULES" != "y" ]
+	then
+		bool "$1" "$2"
+	else
+		eval $2=\${$2:-'n'}  x=\$$2
+	
+		case $x in
+		y) yes='ON'  no='OFF' module='OFF' flag="*"
+   		;;
+		m) yes='OFF' no='OFF' module='ON' flag="M"
+   		;;
+		*) yes='OFF' no='ON'  module='OFF' flag=" "
+   		;;
+		esac
+	
+		echo -ne "'$2' '($flag) $1' " >>submenu
+	
+		echo -e "
+		function $2 () { \
+			l_tristate '$1' '$yes' '$no' '$module' '$2'
+		}"  >>radiolists
+	fi
+}
+
+#
+# Handle a tristate (Yes/No/Module) option.
+#
+function l_tristate () {
+	while true
+	do
+		if $DIALOG --title "$1" \
+			--backtitle "$backtitle" \
+			--radiolist "$radiolist_instructions" 13 70 3 \
+			'y' 'Yes' $2 'n' 'No' $3 'm' 'Module' $4 \
+			2>dialog.out
+		then
+			eval $5=`cat dialog.out`
+			break
+		fi
+			
+		help "$1" "$5"
+	done
+	comments_ok=TRUE
+}
+
+#
+# Create a tristate radiolist function which is dependent on
+# another kernel configuration option.
+#
+# Quote from the original configure script:
+#
+#       If the option we depend upon is a module,
+#       then the only allowable options are M or N.  If Y, then
+#       this is a normal tristate.  This is used in cases where modules
+#       are nested, and one module requires the presence of something
+#       else in the kernel.
+#
+function dep_tristate () {
+	if [ "$CONFIG_MODULES" != "y" ]
+	then
+		bool "$1" "$2"
+	else
+		if  eval [ "_$3" != "_m" ]
+		then
+			tristate "$1" "$2" $3
+		else
+			mod_bool "$1" "$2"
+		fi
+	fi
+}
+
+#
+# Create a function which will call our local int function.
+# 
+function int () {
+	eval $2=\${$2:-"$3"} x=\$$2
+
+	echo -ne "'$2' '($x) $1' " >>submenu
+
+	echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' }\n" >>radiolists
+}
+
+#
+# Create a dialog for entering an integer into a kernel option.
+#
+function l_int () {
+	while true
+	do
+		if $DIALOG --title "$1" \
+			--backtitle "$backtitle" \
+			--inputbox "$inputbox_instructions_int" \
+			15 55 "$4" 2>dialog.out
+		then
+			answer="`cat dialog.out`"
+
+			case "$answer" in
+			[1-9] |\
+			[1-9][0-9] |\
+			[1-9][0-9][0-9] |\
+			[1-9][0-9][0-9][0-9] |\
+			[1-9][0-9][0-9][0-9][0-9])\
+				eval $2="$answer"
+				;;
+			*)	eval $2="$3"
+				echo -en "\007"
+				${DIALOG} --backtitle "$backtitle" \
+					--infobox "You have made an invalid entry." 3 43
+				sleep 2
+				;;
+			esac
+
+			break
+		fi
+
+		help "$1" "$2"
+	done
+	comments_ok=TRUE
+}
+
+
+#
+# Create a function which will call our local int function.
+# 
+function hex () {
+	eval $2=\${$2:-"$3"} x=\${$2#*[x,X]}
+
+	echo -ne "'$2' '($x) $1' " >>submenu
+
+	echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' }\n" >>radiolists
+}
+
+#
+# Create a dialog for entering a hexidecimal into a kernel option.
+#
+function l_hex () {
+	while true
+	do
+		if $DIALOG --title "$1" \
+			--backtitle "$backtitle" \
+			--inputbox "$inputbox_instructions_hex" \
+			15 55 "$4" 2>dialog.out
+		then
+			answer="`cat dialog.out`"
+			answer="${answer#*[x,X]}"
+
+			case "$answer" in
+			[0-9,a-f,A-F] |\
+			[0-9,a-f,A-F][0-9,a-f,A-F] |\
+			[0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F] |\
+			[0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F])\
+				eval $2="$answer"
+				;;
+			*)	eval $2="$3"
+				echo -en "\007"
+				${DIALOG} --backtitle "$backtitle" \
+					--infobox "You have made an invalid entry." 3 43
+				sleep 2
+				;;
+			esac
+
+			break
+		fi
+
+		help "$1" "$2"
+	done
+	comments_ok=TRUE
+}
+
+#
+# Create a function which will call our local One-of-Many choice list.
+#
+function choice () {
+	#
+	# Need to remember params cause the're gonna get reset.
+	#
+	title=$1
+	choices=$2
+	default=$3
+	current=
+
+	#
+	# Find out if one of the choices is already set.
+	# If it's not then make it the default.
+	#
+	set -- $choices
+	firstchoice=$2
+
+	while [ -n "$2" ]
+	do
+		if eval [ "_\$$2" = "_y" ]
+		then
+			current=$1
+			break
+		fi
+		shift ; shift
+	done
+
+	: ${current:=$default}
+
+	echo -ne "'$firstchoice' '($current) $title' " >>submenu
+
+	echo -e "
+	function $firstchoice () {
+		l_choice '$title' \"$choices\" $current
+	}\n" >>radiolists
+}
+
+
+function l_choice () {
+	#
+	# Need to remember params cause the're gonna get reset.
+	#
+	title="$1"
+	choices="$2"
+	current="$3"
+
+	#
+	# Scan current value of choices and set radiolist switches.
+	#
+	list=
+	set -- $choices
+	firstchoice=$2
+	while [ -n "$2" ]
+	do
+		case "$1" in
+		"$current")	list="$list $2 $1 ON "  ;;
+		*)		list="$list $2 $1 OFF " ;;
+		esac
+			
+		shift ; shift
+	done
+
+	while true
+	do
+		if $DIALOG --title "$title" \
+			--backtitle "$backtitle" \
+			--radiolist "$radiolist_instructions" \
+			22 70 11 $list 2>dialog.out
+		then
+			choice=`cat dialog.out`
+			break
+		fi
+
+		help "$title" "$firstchoice"
+	done
+
+	#
+	# Now set the boolean value of each option base on
+	# the selection made from the radiolist.
+	#
+	set -- $choices
+	while [ -n "$2" ]
+	do
+		if [ "$2" = "$choice" ]
+		then
+			eval $2="y"
+		else
+			eval $2="n"
+		fi
+		
+		shift ; shift
+	done
+	comments_ok=TRUE
+}
+
+
+#
+# Now parse the configuration files and create our mini scripts.
+# Each script represents a separate menu which when taken together
+# form a linked menu tree.  Since one configuration file
+# may source another elsewhere in the kernel source tree, this 
+# function is recursive.
+#
+function parse_config_files () {
+	while read command args
+	do
+		if [ "$command" != "comment" -a "$in_a_comment" ]
+		then
+			in_a_comment=
+			echo >>$submenu
+		fi
+
+		case $command in
+
+		#
+		# Will it ever happen that mainmenu_option will be followed
+		# by anything other then "next_comment"?  This assumes not..
+		#
+		mainmenu_option) 
+			comment_is_option=TRUE
+			;;
+
+		comment)
+			if [ "$comment_is_option" ]
+			then
+				comment_is_option=
+
+				stack="$submenu$stack"
+
+				menu_no=$[menu_no + 1]
+				x="submenu$menu_no"
+
+				echo "submenu $args $x" >> $submenu
+
+				submenu=$x
+				echo menu_name $args >$submenu
+
+			elif [ "$in_a_comment" ]
+			then
+				echo -n $args >> $submenu
+			else
+				in_a_comment=TRUE
+				echo -n comment $args >> $submenu
+			fi
+			;;
+	
+		endmenu)
+			submenu="${stack%%*}"
+			stack="${stack#*}"
+			;;
+
+		mainmenu_name)
+			echo menu_name "'Main Menu'" > submenu0
+			;;
+
+		\$MAKE) echo "yuck1 'Configure (You must do this!)' '$args'"\
+				>>$submenu
+			;;
+
+		source)
+			parse_config_files "$args"
+			;;
+
+		'#'|'')  : ;;
+
+		*)	echo $command $args >> $submenu
+			;;
+		esac
+	done < $1
+}
+
+#
+# This is the menu tree's bootstrap.
+#
+# Executes a mini script to create a set of functions, one per configuration
+# option.  These functions will in turn execute dialog commands or recursively
+# call other mini scripts.
+#
+function activate_menu () {
+	comments_ok=
+
+	while true
+	do
+		$1 "$default"		#Create the radiolists and dialog cmd
+		. radiolists		#Read in the dialog functions.
+
+		. submenu 2>dialog.out	#Activate this menu
+
+		case "$?" in
+		0)
+			defaults="`cat dialog.out`$defaults"  #pseudo stack
+			source dialog.out
+			default="${defaults%%*}" defaults="${defaults#*}"
+			;;
+		2)	
+			read selection <dialog.out
+			default="${selection##* }"
+			case "$selection" in
+			*"-->"*) : ;;
+			*)	 eval help $selection ;;
+			esac
+			;;
+		255|1)
+			break
+			;;
+		esac
+	done
+}
+
+#
+# Just what it says.
+#
+save_configuration () {
+	${DIALOG} --backtitle "$backtitle" \
+		  --infobox "Saving your new kernel configuration..."  3 43
+
+	#
+	# Now, let's redefine the configuration functions for final
+	# output to the config files.
+	#
+	function bool () {
+		eval define_bool "$2" "\${$2:-n}"
+	}
+
+	function tristate () {
+		eval define_bool "$2" "\${$2:-n}"
+	}
+
+	function dep_tristate () {
+		eval x=\${$2:-n}
+		if eval [ "_$3" != "_m" ]
+		then
+			tristate "$1" "$2"
+		else
+			if [ "$x" = "y" ]
+			then
+				x="m"
+			fi
+			define_bool "$2" "$x"
+		fi
+	}
+
+	function int () {
+		eval x=\${$2:-"$3"}
+		echo "$2=$x" 		>>$CONFIG
+		echo "#define $2 ($x)"	>>$CONFIG_H
+	}
+
+	function hex () {
+		eval x=\${$2:-"$3"}
+		echo "$2=$x" 			>>$CONFIG
+		echo "#define $2 0x${x#*[x,X]}"	>>$CONFIG_H
+	}
+
+	function define_bool () {
+   		case "$2" in
+         	y)
+                	echo "$1=y" 		>>$CONFIG
+                	echo "#define $1 1"	>>$CONFIG_H
+                	;;
+
+         	m)
+			if [ "$CONFIG_MODULES" = "y" ]
+			then
+                		echo "$1=m"		>>$CONFIG
+                		echo "#undef  $1"	>>$CONFIG_H
+			else
+                		echo "$1=y" 		>>$CONFIG
+                		echo "#define $1 1"	>>$CONFIG_H
+			fi
+                	;;
+
+         	n)
+                	echo "# $1 is not set"	>>$CONFIG
+                	echo "#undef  $1"	>>$CONFIG_H
+                	;;
+        	esac
+	}
+
+	function choice () {
+		#
+		# Find the first choice that's already set to 'y'
+		#
+		choices="$2"
+		default="$3"
+		current=
+
+		set -- $choices
+		while [ -n "$2" ]
+		do
+			if eval [ "_\$$2" = "_y" ]
+			then
+				current=$1
+				break
+			fi
+			shift ; shift
+		done
+
+		#
+		# Use the default if none were set.  
+		#
+		: ${current:=$default}
+
+		#
+		# Then extract the actual option from the list of choices.
+		#
+		current=${choices#*$current} ; set $current
+
+		define_bool "$1" "y"
+	}
+
+	function mainmenu_name () {
+		:
+	}
+
+	function mainmenu_option () {
+		comment_is_option=TRUE
+	}
+
+	function endmenu () {
+		:
+	}
+
+	function comment () {
+		if [ "$comment_is_option" ]
+		then
+			comment_is_option=
+			echo        >>$CONFIG
+			echo "#"    >>$CONFIG
+			echo "# $1" >>$CONFIG
+			echo "#"    >>$CONFIG
+
+			echo         >>$CONFIG_H
+			echo "/*"    >>$CONFIG_H
+			echo " * $1" >>$CONFIG_H
+			echo " */"   >>$CONFIG_H
+		fi
+	}
+
+	#
+	# This will hopfully prevent the sound configuration from
+	# running again.  (blagh!)
+	#
+	MAKE=:
+
+	CONFIG=.tmpconfig
+	CONFIG_H=.tmpconfig.h
+
+	echo "#" >$CONFIG
+	echo "# Automatically generated by make menuconfig: don't edit" >>$CONFIG
+	echo "#" >>$CONFIG
+
+	echo "/*" >$CONFIG_H
+	echo " * Automatically generated by make menuconfig: don't edit" >>$CONFIG_H
+	echo " */" >>$CONFIG_H
+	
+	. $CONFIG_IN
+
+	if [ -f .config ]
+	then
+		rm -f .config.old
+		mv .config .config.old
+	fi
+	mv .tmpconfig .config
+	mv .tmpconfig.h include/linux/autoconf.h
+}
+
+#
+# Remove temporary files
+#
+cleanup () {
+	cleanup1
+	cleanup2
+}
+
+cleanup1 () {
+	rm -f submenu* radiolists dialog.out help.out
+}
+
+cleanup2 () {
+	rm -f .tmpconfig .tmpconfig.h
+}
+
+
+menu_instructions="\
+Arrow keys navigate the menu.  \
+Highlighted letters are shortcuts. \
+Select an item with <Space Bar> or <Enter>. \
+When finished press <E> or <X> or <Esc>.  \
+(*) indicates an option will be compiled into the kernel.  \
+(M) indicates an option will be compiled as a module."
+
+radiolist_instructions="\
+Use the arrow keys to navigate this radiolist or \
+press the first letter of the item you wish to select \
+followed by the <SPACE BAR>.
+Press <H> for additional information about this option."
+
+inputbox_instructions_int="\
+Please enter a decimal value between 1 and 9999. \
+Fractions will not be accepted.  \
+Use the <TAB> key to move from the input field to buttons below it."
+
+inputbox_instructions_hex="\
+Please enter a hexidecimal value. \
+Use the <TAB> key to move from the input field to buttons below it."
+
+backtitle="Linux Kernel Configuration"
+
+DIALOG="./scripts/lxdialog/lxdialog"
+
+comment_is_option=
+in_a_comment=
+comments_ok=
+menu_no=0
+submenu=submenu0
+kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}"
+
+trap "cleanup ; rm -f .menuconfig.in ; exit 1" 1 2 15
+
+#
+# Locate default files.
+#
+DEFAULT=""
+if [ "$1" = "-d" ] ; then
+	DEFAULT="-d"
+	shift
+fi
+
+CONFIG_IN=./config.in
+if [ "$1" != "" ] ; then
+	CONFIG_IN=$1
+fi
+
+DEFAULTS=arch/$ARCH/defconfig
+if [ -f .config ]; then
+  DEFAULTS=.config
+fi
+
+if [ -f $DEFAULTS ]; then
+  echo "#"
+  echo "# Using defaults found in" $DEFAULTS
+  echo "#"
+  . $DEFAULTS
+else
+  echo "#"
+  echo "# No defaults found"
+  echo "#"
+fi
+
+
+#
+# Convert the configuration files into our mini scripts.
+# Then put all the mini scripts into a single file which should
+# stick around and act as a cache until the kernel release changes
+# or "make mrproper".
+#
+if [ -e .menuconfig.in ]
+then
+	read x <.menuconfig.in
+	if [ "$x" != "# $kernel_version" ]
+	then
+		rm -f .menuconfig.in
+	fi
+fi
+
+if [ ! -e .menuconfig.in ]
+then
+	$DIALOG	--backtitle "$backtitle" \
+		--infobox "Compiling configuration script..." 3 40
+
+	rm -f submenu*
+	parse_config_files $CONFIG_IN
+
+	#
+	# For handling Sound configuration. (Yuk!)
+	#
+	echo '$MAKE $*' >submenu999
+
+	echo "# $kernel_version" >.menuconfig.in
+
+	for i in submenu*
+	do
+		echo "function $i () {"
+		echo 'default=$1'
+		cat $i
+		echo -e "}\n\n"
+	done >> .menuconfig.in
+
+	rm -f submenu*
+fi
+
+source .menuconfig.in
+
+#
+# Start the ball rolling from the top.
+#
+activate_menu submenu0
+
+#
+# We're done with the menu scripts.  Get rid of them.
+#
+cleanup1
+
+#
+# Confirm and Save
+#
+if $DIALOG --backtitle "$backtitle" \
+	   --yesno "Do you wish to save your new kernel configuration?" 5 60
+	   
+then
+	save_configuration
+
+	$DIALOG --backtitle "$backtitle" --infobox "\
+The linux kernel is now hopefully configured for your setup.  \
+Check the top-level Makefile for additional configuration, \
+and do a 'make dep ; make clean' if you want to be sure all \
+the files are correctly re-made." 7 60
+else
+	$DIALOG --backtitle "$backtitle" \
+		--infobox "Your new configuration was not saved." 3 42 
+	
+fi
+
+
+exit 0
+
diff -u --recursive --new-file v1.3.59/linux/scripts/README.Menuconfig linux/scripts/README.Menuconfig
--- v1.3.59/linux/scripts/README.Menuconfig	Thu Jan  1 02:00:00 1970
+++ linux/scripts/README.Menuconfig	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,106 @@
+This is the Linux kernel menuconfig README file.
+
+Menuconfig gives the linux kernel configuration a long needed face lift.
+It features text based color menus, radiolists and dialogs, but does
+not require X Windows.  With this utility you can easily select a
+kernel option to modify without having to sift through 100 other options.
+
+The windowing support utility (lxdialog) is a VERY modified version of the
+dialog utility by Savio Lam <lam836@cs.cuhk.hk>.  Although lxdialog is 
+significantly different from dialog, I have left Savio's copyrights intact.
+Please DO NOT contact Savio with questions about lxdialog.  He will not be
+able to assist.
+
+
+Please feel free to send any questions or comments or suggestions to
+William Roadcap <roadcapw@cfw.com>.
+
+
+Some menuconfig keyboard hints:
+
+Menus
+----------
+o  Use the Up/Down arrow keys (cursor keys) to highlight the item 
+   or submenu you wish to select.  
+   Shortcut: Press the option's highlighted letter. (usually the first)
+
+o  Use the cursor keys to highlight <Select> and press <ENTER>.  
+   Shortcut: Press <Space Bar> or <S> if there's no item beginning with <S>.
+
+o  To exit a menu use the cursor keys to highlight the <Exit> button and 
+   press <ENTER>.  
+   Shortcut: Press <Esc> or <E> or <X> if there's no option beginning 
+             those letters.
+
+   Also, the <TAB> key will toggle between <Select>, <Exit> and <Help>
+
+o  To get help with an item, use the cursor keys to highlight <Help> and
+   Press <ENTER>.
+   Shortcut: Press <H> or <?>.
+
+
+Radiolists  (Yes/No/Module)
+-----------
+o  Use the cursor keys to select the option you wish to set and press
+   <O> or the <SPACE BAR>.
+   Shortcut: Press the first letter of the option you wish to set then
+             press <O> or <SPACE BAR>.
+
+o  To see available help for the item, use the cursor keys to highlight
+   <Help> and Press <ENTER>.
+   Shortcut: Press <H> or <?>.
+
+   Also, the <TAB> key will toggle between <Select> and <Help>
+
+
+Data Entry
+-----------
+o  Enter the requested information and press <ENTER>
+
+o  For help, use the <TAB> key to highlight the help option and 
+   press <ENTER>.  You can try <TAB><H> as well.
+
+
+Text Box    (Help Window)
+--------
+o  Use the cursor keys to scroll up/down/left/right.
+
+o  Press <E>, <X>, <Enter> or <Esc> to exit.
+
+
+Final Acceptance
+----------------
+With the exception of sound configuration, YOUR CHANGES ARE NOT FINAL.
+You will be given a last chance to confirm them prior to exiting menuconfig.
+
+
+
+Other information:
+
+Menuconfig attempts to reduce the amount of time it takes to reconfigure
+the kernel. 
+
+Menuconfig keeps a cache of it's compiled configuration scripts in 
+/usr/src/linux/.menuconfig.in.  This file will only be rebuilt if you
+do a 'make mrproper' or if the kernel version changes.  If you make manual 
+changes to any of the config.in files in the kernel source tree, you 
+must remove .menuconfig.in so that menuconfig will know to rebuild it.
+
+The windowing utility, lxdialog, will only be rebuilt if your kernel
+source tree is fresh, or changes are patched into it via a kernel patch
+or you do 'make mrproper'.  If changes to lxdialog are patched in, most
+likely the rebuild time will be short.  You may force a complete rebuild
+of lxdialog by changing to it's directory and doing 'make clean all'
+
+NOTICE:  lxdialog requires the ncurses libraries to compile.  If you
+         don't already have ncurses you really should get it.
+
+         The makefile for lxdialog attempts to find your ncurses header
+         file.  Although it should find the header for older versions of
+         ncurses, it is probably a good idea to get the latest ncurses
+         anyway.
+
+WARNING: It is not recommended that you change any defines in lxdialog's
+         header files.
+
+
diff -u --recursive --new-file v1.3.59/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.59/linux/scripts/header.tk	Thu Jan  4 21:55:00 1996
+++ linux/scripts/header.tk	Wed Feb  7 08:56:04 1996
@@ -5,7 +5,7 @@
 set winy 200
 
 #
-# Define some macros we will need to parse the kmenu-config.dat file.
+# Define some macros we will need to parse the config.in file.
 #
 proc mainmenu_name { text } {
 	message .header.message -width 400 -relief raised -bg grey -text "$text"
@@ -230,12 +230,12 @@
 proc bool {w mnum line text variable} {
 	toggle_switch $w $mnum $line $text $variable
 	$w.x$line.m configure -state disabled
-	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+	pack $w.x$line -anchor w -fill both -expand on
 }
 
 proc tristate {w mnum line text variable } {
 	toggle_switch $w $mnum $line $text $variable
-	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+	pack $w.x$line -anchor w -fill both -expand on
 }
 
 proc dep_tristate {w mnum line text variable } {
@@ -248,7 +248,7 @@
 	entry $w.x$line.x -width 15 -relief sunken -borderwidth 2 \
 		-textvariable $variable
 	pack $w.x$line.x -anchor w -side right -fill y
-	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+	pack $w.x$line -anchor w -fill both -expand on
 }
 
 proc minimenu { w mnum line text variable } {
@@ -257,7 +257,7 @@
 	menubutton $w.x$line.x -text "Choose..." -menu $w.x$line.x.menu \
 		-relief raised -width 15
 	pack $w.x$line.x -anchor w -side right -fill y
-	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+	pack $w.x$line -anchor w -fill both -expand on
 }
 
 proc comment {w line text } {
diff -u --recursive --new-file v1.3.59/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
--- v1.3.59/linux/scripts/ksymoops.cc	Fri Oct  6 14:35:16 1995
+++ linux/scripts/ksymoops.cc	Wed Feb  7 08:56:04 1996
@@ -254,6 +254,7 @@
 int
 main(int argc, char** argv)
 {
+    char c;
     program_name = (argc--, *argv++);
 
     NameList names;
@@ -280,9 +281,9 @@
 	cin >> buffer;
 	if (strequ(buffer, "EIP:") && names.valid()) {
 	    cin >> ::hex >> eip_addr;
-	    cin >> buffer[0];
+	    cin >> c >> c >> c;
 	    cin >> ::hex >> eip_addr;
-	    cin >> buffer;
+	    cin >> c >> c >> buffer;
 	    if (!strequ(buffer, "EFLAGS:")) {
 		clog << "Please strip the line-prefixes and rerun " << program_name << endl;
 		exit(1);
@@ -292,7 +293,9 @@
 		cout << ">>EIP: " << *ksym << endl;
 	} else if (strequ(buffer, "Trace:") && names.valid()) {
 	    long address;
-	    while ((cin >> ::hex >> address) && address > 0xc) {
+	    while ((cin >> buffer) && 
+		   (sscanf(buffer, " [<%x>]", &address) == 1) &&
+		   address > 0xc) {
 		cout << "Trace: ";
 		KSym* ksym = names.find(address);
 		if (ksym)
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/BIG.FAT.WARNING linux/scripts/lxdialog/BIG.FAT.WARNING
--- v1.3.59/linux/scripts/lxdialog/BIG.FAT.WARNING	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/BIG.FAT.WARNING	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,4 @@
+This is NOT the official version of dialog.  This version has been
+significantly modified from the original.  It is for use by the Linux
+kernel configuration script.  Please do not bother Savio Lam with 
+questions about this program.
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/Makefile linux/scripts/lxdialog/Makefile
--- v1.3.59/linux/scripts/lxdialog/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/Makefile	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,42 @@
+CC = gcc
+CPP = gcc -E
+OPTIM = -O2 -Wall -fomit-frame-pointer
+
+CFLAGS = $(OPTIM) $(CURSES) -DLOCALE 
+LDFLAGS = -s -L .
+LDLIBS = -lncurses
+
+CURSES =  -DCURSES_LOC="<curses.h>"
+
+ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h))
+        CFLAGS += -I/usr/include/ncurses
+        CURSES =  -DCURSES_LOC="<ncurses.h>"
+else
+ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h))
+        CURSES =  -DCURSES_LOC="<ncurses.h>"
+endif
+endif
+
+OBJS = checklist.o menubox.o textbox.o yesno.o inputbox.o \
+       util.o lxdialog.o msgbox.o
+SRCS = $(OBJS:.o=.c)
+
+
+all: ncurses lxdialog
+
+lxdialog: $(OBJS)
+
+ncurses:
+	@x=`find /lib /usr/lib /usr/local/lib -maxdepth 1 -name 'libncurses.*'` ;\
+	if [ ! "$$x" ]; then \
+		echo -e "\007" ;\
+		echo ">> Unable to find the Ncurses libraries." ;\
+		echo ">>" ;\
+		echo ">> You must have Ncurses installed in order" ;\
+		echo ">> to use 'make menuconfig'" ;\
+		echo ;\
+		exit 1 ;\
+	fi
+
+clean:
+	rm -f core *.o *~ lxdialog
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/checklist.c linux/scripts/lxdialog/checklist.c
--- v1.3.59/linux/scripts/lxdialog/checklist.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/checklist.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,361 @@
+/*
+ *  checklist.c -- implements the checklist box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *     Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+ *     Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+static int list_width, check_x, item_x, checkflag;
+
+/*
+ * Print list item
+ */
+static void
+print_item (WINDOW * win, const char *item, int status,
+	    int choice, int selected)
+{
+    int i;
+
+    /* Clear 'residue' of last item */
+    wattrset (win, menubox_attr);
+    wmove (win, choice, 0);
+    for (i = 0; i < list_width; i++)
+	waddch (win, ' ');
+
+    wmove (win, choice, check_x);
+    wattrset (win, selected ? check_selected_attr : check_attr);
+    if (checkflag == FLAG_CHECK)
+	wprintw (win, "[%c]", status ? 'X' : ' ');
+    else
+	wprintw (win, "(%c)", status ? 'X' : ' ');
+
+    wattrset (win, selected ? tag_selected_attr : tag_attr);
+    mvwaddch(win, choice, item_x, item[0]);
+    wattrset (win, selected ? item_selected_attr : item_attr);
+    waddstr (win, (char *)item+1);
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void
+print_arrows (WINDOW * win, int choice, int item_no, int scroll,
+		int y, int x, int height)
+{
+    wmove(win, y, x);
+
+    if (scroll > 0) {
+	wattrset (win, uarrow_attr);
+	waddch (win, ACS_UARROW);
+	waddstr (win, "(-)");
+    }
+    else {
+	wattrset (win, menubox_attr);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+    }
+
+   y = y + height + 1;
+   wmove(win, y, x);
+
+   if ((height < item_no) && (scroll + choice < item_no - 1)) {
+	wattrset (win, darrow_attr);
+	waddch (win, ACS_DARROW);
+	waddstr (win, "(+)");
+    }
+    else {
+	wattrset (win, menubox_border_attr);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+   }
+}
+
+/*
+ *  Display the termination buttons
+ */
+static void
+print_buttons( WINDOW *dialog, int height, int width, int okval, int cancelval)
+{
+    int x = width / 2 - 11;
+    int y = height - 2;
+
+    print_button (dialog, "Select", y, x, okval);
+    print_button (dialog, " Help ", y, x + 14, cancelval);
+
+    wrefresh (dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * The `flag' parameter is used to select between radiolist and checklist.
+ */
+int
+dialog_checklist (const char *title, const char *prompt, int height, int width,
+	int list_height, int item_no, const char * const * items, int flag,
+	int separate_output)
+{
+    int i, x, y, cur_x, cur_y, box_x, box_y;
+    int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
+    WINDOW *dialog, *list;
+
+    checkflag = flag;
+
+    /* Allocate space for storing item on/off status */
+    if ((status = malloc (sizeof (int) * item_no)) == NULL) {
+	endwin ();
+	fprintf (stderr,
+		 "\nCan't allocate memory in dialog_checklist().\n");
+	exit (-1);
+    }
+
+    /* Initializes status */
+    for (i = 0; i < item_no; i++) {
+	status[i] = !strcasecmp (items[i * 3 + 2], "on");
+	if (!choice && status[i])
+            choice = i;
+    }
+
+    max_choice = MIN (list_height, item_no);
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+	waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    list_width = width - 6;
+    getyx (dialog, cur_y, cur_x);
+    box_y = height - list_height - 5;
+    box_x = (width - list_width) / 2 - 1;
+
+    /* create new window for the list */
+    list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1);
+
+    keypad (list, TRUE);
+
+    /* draw a box around the list items */
+    draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2,
+	      menubox_border_attr, menubox_attr);
+
+    /* Find length of longest item in order to center checklist */
+    check_x = 0;
+    for (i = 0; i < item_no; i++) 
+	check_x = MAX (check_x, + strlen (items[i * 3 + 1]) + 4);
+
+    check_x = (list_width - check_x) / 2;
+    item_x = check_x + 4;
+
+    /* Print the list */
+    for (i = 0; i < max_choice; i++)
+	print_item (list, items[i * 3 + 1], status[i], i, i == choice);
+
+    wnoutrefresh (list);
+
+    print_arrows(dialog, choice, item_no, scroll,
+			box_y, box_x + check_x + 5, list_height);
+
+    print_buttons(dialog, height, width, TRUE, FALSE);
+
+    while (key != ESC) {
+	key = wgetch (dialog);
+
+    	for (i = 0; i < max_choice; i++)
+            if (toupper(key) == toupper(items[(scroll+i)*3+1][0]))
+                break;
+
+
+	if ( i < max_choice || key == KEY_UP || key == KEY_DOWN || 
+	    key == '+' || key == '-' ) {
+	    if (key == KEY_UP || key == '-') {
+		if (!choice) {
+		    if (!scroll)
+			continue;
+		    /* Scroll list down */
+		    getyx (dialog, cur_y, cur_x);
+		    if (list_height > 1) {
+			/* De-highlight current first item */
+			print_item (list, items[scroll * 3 + 1],
+					status[scroll], 0, FALSE);
+			scrollok (list, TRUE);
+			wscrl (list, -1);
+			scrollok (list, FALSE);
+		    }
+		    scroll--;
+		    print_item (list, items[scroll * 3 + 1],
+				status[scroll], 0, TRUE);
+		    wnoutrefresh (list);
+
+    		    print_arrows(dialog, choice, item_no, scroll,
+				box_y, box_x + check_x + 5, list_height);
+
+		    wmove (dialog, cur_y, cur_x);
+		    wrefresh (dialog);
+
+		    continue;	/* wait for another key press */
+		} else
+		    i = choice - 1;
+	    } else if (key == KEY_DOWN || key == '+') {
+		if (choice == max_choice - 1) {
+		    if (scroll + choice >= item_no - 1)
+			continue;
+		    /* Scroll list up */
+		    getyx (dialog, cur_y, cur_x);
+		    if (list_height > 1) {
+			/* De-highlight current last item before scrolling up */
+			print_item (list, items[(scroll + max_choice - 1) * 3 + 1],
+				    status[scroll + max_choice - 1],
+				    max_choice - 1, FALSE);
+			scrollok (list, TRUE);
+			scroll (list);
+			scrollok (list, FALSE);
+		    }
+		    scroll++;
+		    print_item (list, items[(scroll + max_choice - 1) * 3 + 1],
+				status[scroll + max_choice - 1],
+				max_choice - 1, TRUE);
+		    wnoutrefresh (list);
+
+    		    print_arrows(dialog, choice, item_no, scroll,
+				box_y, box_x + check_x + 5, list_height);
+
+		    wmove (dialog, cur_y, cur_x);
+		    wrefresh (dialog);
+
+		    continue;	/* wait for another key press */
+		} else
+		    i = choice + 1;
+	    }
+	    if (i != choice) {
+		/* De-highlight current item */
+		getyx (dialog, cur_y, cur_x);
+		print_item (list, items[(scroll + choice) * 3 + 1],
+			    status[scroll + choice], choice, FALSE);
+		/* Highlight new item */
+		choice = i;
+		print_item (list, items[(scroll + choice) * 3 + 1],
+			    status[scroll + choice], choice, TRUE);
+		wnoutrefresh (list);
+		wmove (dialog, cur_y, cur_x);
+		wrefresh (dialog);
+	    }
+	    continue;		/* wait for another key press */
+	}
+	switch (key) {
+	case 'H':
+	case 'h':
+	case '?':
+	    delwin (dialog);
+	    free (status);
+	    return 1;
+	    return 1;
+	case TAB:
+	case KEY_LEFT:
+	case KEY_RIGHT:
+	    if (!button) {
+		button = 1;	/* "Help" button selected */
+		print_buttons(dialog, height, width, FALSE, TRUE);
+	    } else {
+		button = 0;	/* "OK" button selected */
+		print_buttons(dialog, height, width, TRUE, FALSE);
+	    }
+	    wrefresh (dialog);
+	    break;
+	case 'S':
+	case 's':
+	case ' ':
+	case '\n':
+	    if (!button) {
+		if (flag == FLAG_CHECK) {
+		    status[scroll + choice] = !status[scroll + choice];
+		    getyx (dialog, cur_y, cur_x);
+		    wmove (list, choice, check_x);
+		    wattrset (list, check_selected_attr);
+		    wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' ');
+		} else {
+		    if (!status[scroll + choice]) {
+			for (i = 0; i < item_no; i++)
+			    status[i] = 0;
+			status[scroll + choice] = 1;
+			getyx (dialog, cur_y, cur_x);
+			for (i = 0; i < max_choice; i++)
+			    print_item (list, items[(scroll + i) * 3 + 1],
+					status[scroll + i], i, i == choice);
+		    }
+		}
+		wnoutrefresh (list);
+		wmove (dialog, cur_y, cur_x);
+		wrefresh (dialog);
+            
+		for (i = 0; i < item_no; i++) {
+		    if (status[i]) {
+			if (flag == FLAG_CHECK) {
+			    if (separate_output) {
+				fprintf (stderr, "%s\n", items[i * 3]);
+			    } else {
+				fprintf (stderr, "\"%s\" ", items[i * 3]);
+			    }
+			} else {
+			    fprintf (stderr, "%s", items[i * 3]);
+			}
+
+		    }
+		}
+            }
+	    delwin (dialog);
+	    free (status);
+	    return button;
+	case 'X':
+	case 'x':
+	    key = ESC;
+	case ESC:
+	    break;
+	}
+    }
+
+    delwin (dialog);
+    free (status);
+    return -1;			/* ESC pressed */
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/colors.h linux/scripts/lxdialog/colors.h
--- v1.3.59/linux/scripts/lxdialog/colors.h	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/colors.h	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,161 @@
+/*
+ *  colors.h -- color attribute definitions
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  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.
+ */
+
+
+/*
+ *   Default color definitions
+ *
+ *   *_FG = foreground
+ *   *_BG = background
+ *   *_HL = highlight?
+ */
+#define SCREEN_FG                    COLOR_CYAN
+#define SCREEN_BG                    COLOR_BLUE
+#define SCREEN_HL                    TRUE
+
+#define SHADOW_FG                    COLOR_BLACK
+#define SHADOW_BG                    COLOR_BLACK
+#define SHADOW_HL                    TRUE
+
+#define DIALOG_FG                    COLOR_BLACK
+#define DIALOG_BG                    COLOR_WHITE
+#define DIALOG_HL                    FALSE
+
+#define TITLE_FG                     COLOR_YELLOW
+#define TITLE_BG                     COLOR_WHITE
+#define TITLE_HL                     TRUE
+
+#define BORDER_FG                    COLOR_WHITE
+#define BORDER_BG                    COLOR_WHITE
+#define BORDER_HL                    TRUE
+
+#define BUTTON_ACTIVE_FG             COLOR_WHITE
+#define BUTTON_ACTIVE_BG             COLOR_BLUE
+#define BUTTON_ACTIVE_HL             TRUE
+
+#define BUTTON_INACTIVE_FG           COLOR_BLACK
+#define BUTTON_INACTIVE_BG           COLOR_WHITE
+#define BUTTON_INACTIVE_HL           FALSE
+
+#define BUTTON_KEY_ACTIVE_FG         COLOR_WHITE
+#define BUTTON_KEY_ACTIVE_BG         COLOR_BLUE
+#define BUTTON_KEY_ACTIVE_HL         TRUE
+
+#define BUTTON_KEY_INACTIVE_FG       COLOR_RED
+#define BUTTON_KEY_INACTIVE_BG       COLOR_WHITE
+#define BUTTON_KEY_INACTIVE_HL       FALSE
+
+#define BUTTON_LABEL_ACTIVE_FG       COLOR_YELLOW
+#define BUTTON_LABEL_ACTIVE_BG       COLOR_BLUE
+#define BUTTON_LABEL_ACTIVE_HL       TRUE
+
+#define BUTTON_LABEL_INACTIVE_FG     COLOR_BLACK
+#define BUTTON_LABEL_INACTIVE_BG     COLOR_WHITE
+#define BUTTON_LABEL_INACTIVE_HL     TRUE
+
+#define INPUTBOX_FG                  COLOR_BLACK
+#define INPUTBOX_BG                  COLOR_WHITE
+#define INPUTBOX_HL                  FALSE
+
+#define INPUTBOX_BORDER_FG           COLOR_BLACK
+#define INPUTBOX_BORDER_BG           COLOR_WHITE
+#define INPUTBOX_BORDER_HL           FALSE
+
+#define SEARCHBOX_FG                 COLOR_BLACK
+#define SEARCHBOX_BG                 COLOR_WHITE
+#define SEARCHBOX_HL                 FALSE
+
+#define SEARCHBOX_TITLE_FG           COLOR_YELLOW
+#define SEARCHBOX_TITLE_BG           COLOR_WHITE
+#define SEARCHBOX_TITLE_HL           TRUE
+
+#define SEARCHBOX_BORDER_FG          COLOR_WHITE
+#define SEARCHBOX_BORDER_BG          COLOR_WHITE
+#define SEARCHBOX_BORDER_HL          TRUE
+
+#define POSITION_INDICATOR_FG        COLOR_YELLOW
+#define POSITION_INDICATOR_BG        COLOR_WHITE
+#define POSITION_INDICATOR_HL        TRUE
+
+#define MENUBOX_FG                   COLOR_BLACK
+#define MENUBOX_BG                   COLOR_WHITE
+#define MENUBOX_HL                   FALSE
+
+#define MENUBOX_BORDER_FG            COLOR_WHITE
+#define MENUBOX_BORDER_BG            COLOR_WHITE
+#define MENUBOX_BORDER_HL            TRUE
+
+#define ITEM_FG                      COLOR_BLACK
+#define ITEM_BG                      COLOR_WHITE
+#define ITEM_HL                      FALSE
+
+#define ITEM_SELECTED_FG             COLOR_WHITE
+#define ITEM_SELECTED_BG             COLOR_BLUE
+#define ITEM_SELECTED_HL             TRUE
+
+#define TAG_FG                       COLOR_YELLOW
+#define TAG_BG                       COLOR_WHITE
+#define TAG_HL                       TRUE
+
+#define TAG_SELECTED_FG              COLOR_YELLOW
+#define TAG_SELECTED_BG              COLOR_BLUE
+#define TAG_SELECTED_HL              TRUE
+
+#define TAG_KEY_FG                   COLOR_YELLOW
+#define TAG_KEY_BG                   COLOR_WHITE
+#define TAG_KEY_HL                   TRUE
+
+#define TAG_KEY_SELECTED_FG          COLOR_YELLOW
+#define TAG_KEY_SELECTED_BG          COLOR_BLUE
+#define TAG_KEY_SELECTED_HL          TRUE
+
+#define CHECK_FG                     COLOR_BLACK
+#define CHECK_BG                     COLOR_WHITE
+#define CHECK_HL                     FALSE
+
+#define CHECK_SELECTED_FG            COLOR_WHITE
+#define CHECK_SELECTED_BG            COLOR_BLUE
+#define CHECK_SELECTED_HL            TRUE
+
+#define UARROW_FG                    COLOR_GREEN
+#define UARROW_BG                    COLOR_WHITE
+#define UARROW_HL                    TRUE
+
+#define DARROW_FG                    COLOR_GREEN
+#define DARROW_BG                    COLOR_WHITE
+#define DARROW_HL                    TRUE
+
+/* End of default color definitions */
+
+#define C_ATTR(x,y)                  ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
+#define COLOR_NAME_LEN               10
+#define COLOR_COUNT                  8
+
+/*
+ * Global variables
+ */
+
+typedef struct {
+    char name[COLOR_NAME_LEN];
+    int value;
+} color_names_st;
+
+extern color_names_st color_names[];
+extern int color_table[][3];
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h
--- v1.3.59/linux/scripts/lxdialog/dialog.h	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/dialog.h	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,168 @@
+/*
+ *  dialog.h -- common declarations for all dialog modules
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  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 <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include CURSES_LOC
+
+
+#define ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/* 
+ * Attribute names
+ */
+#define screen_attr                   attributes[0]
+#define shadow_attr                   attributes[1]
+#define dialog_attr                   attributes[2]
+#define title_attr                    attributes[3]
+#define border_attr                   attributes[4]
+#define button_active_attr            attributes[5]
+#define button_inactive_attr          attributes[6]
+#define button_key_active_attr        attributes[7]
+#define button_key_inactive_attr      attributes[8]
+#define button_label_active_attr      attributes[9]
+#define button_label_inactive_attr    attributes[10]
+#define inputbox_attr                 attributes[11]
+#define inputbox_border_attr          attributes[12]
+#define searchbox_attr                attributes[13]
+#define searchbox_title_attr          attributes[14]
+#define searchbox_border_attr         attributes[15]
+#define position_indicator_attr       attributes[16]
+#define menubox_attr                  attributes[17]
+#define menubox_border_attr           attributes[18]
+#define item_attr                     attributes[19]
+#define item_selected_attr            attributes[20]
+#define tag_attr                      attributes[21]
+#define tag_selected_attr             attributes[22]
+#define tag_key_attr                  attributes[23]
+#define tag_key_selected_attr         attributes[24]
+#define check_attr                    attributes[25]
+#define check_selected_attr           attributes[26]
+#define uarrow_attr                   attributes[27]
+#define darrow_attr                   attributes[28]
+
+/* number of attributes */
+#define ATTRIBUTE_COUNT               29
+
+/*
+ * Global variables
+ */
+extern bool use_colors;
+extern bool use_shadow;
+
+extern chtype attributes[];
+
+extern const char *backtitle;
+
+/*
+ * Function prototypes
+ */
+extern void create_rc (const char *filename);
+extern int parse_rc (void);
+
+
+void init_dialog (void);
+void end_dialog (void);
+void attr_clear (WINDOW * win, int height, int width, chtype attr);
+void dialog_clear (void);
+void color_setup (void);
+void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button (WINDOW * win, const char *label, int y, int x, int selected);
+void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box,
+		chtype border);
+void draw_shadow (WINDOW * win, int y, int x, int height, int width);
+
+int first_alpha (const char *string);
+int dialog_yesno (const char *title, const char *prompt, int height, int width);
+int dialog_msgbox (const char *title, const char *prompt, int height,
+		int width, int pause);
+int dialog_textbox (const char *title, const char *file, int height, int width);
+int dialog_menu (const char *title, const char *prompt, int height, int width,
+		int menu_height, const char *choice, int item_no, 
+		const char * const * items);
+int dialog_checklist (const char *title, const char *prompt, int height,
+		int width, int list_height, int item_no,
+		const char * const * items, int flag, int separate_output);
+extern unsigned char dialog_input_result[];
+int dialog_inputbox (const char *title, const char *prompt, int height,
+		int width, const char *init);
+int dialog_guage (const char *title, const char *prompt, int height, int width,
+		int percent);
+
+/*
+ * This is the base for fictious keys, which activate
+ * the buttons.
+ *
+ * Mouse-generated keys are the following:
+ *   -- the first 32 are used as numbers, in addition to '0'-'9'
+ *   -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+ *   -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+ */
+#define M_EVENT (KEY_MAX+1)
+
+
+/*
+ * The `flag' parameter in checklist is used to select between
+ * radiolist and checklist
+ */
+#define FLAG_CHECK 1
+#define FLAG_RADIO 0
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/inputbox.c linux/scripts/lxdialog/inputbox.c
--- v1.3.59/linux/scripts/lxdialog/inputbox.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/inputbox.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,231 @@
+/*
+ *  inputbox.c -- implements the input box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+unsigned char dialog_input_result[MAX_LEN + 1];
+
+/*
+ *  Print the termination buttons
+ */
+static void
+print_buttons(WINDOW *dialog, int height, int width, int okval, int cancelval)
+{
+    int x = width / 2 - 11;
+    int y = height - 2;
+
+    print_button (dialog, "  Ok  ", y, x, okval);
+    print_button (dialog, " Help ", y, x + 14, cancelval);
+
+    wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box for inputing a string
+ */
+int
+dialog_inputbox (const char *title, const char *prompt, int height, int width,
+		 const char *init)
+{
+    int i, x, y, box_y, box_x, box_width;
+    int input_x = 0, scroll = 0, key = 0, button = -1;
+    unsigned char *instr = dialog_input_result;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+	waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    /* Draw the input field box */
+    box_width = width - 6;
+    getyx (dialog, y, x);
+    box_y = y + 2;
+    box_x = (width - box_width) / 2;
+    draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2,
+	      border_attr, dialog_attr);
+
+    print_buttons(dialog, height, width, TRUE, FALSE);
+
+    /* Set up the initial value */
+    wmove (dialog, box_y, box_x);
+    wattrset (dialog, inputbox_attr);
+
+    if (!init)
+	instr[0] = '\0';
+    else
+	strcpy (instr, init);
+
+    input_x = strlen (instr);
+
+    if (input_x >= box_width) {
+	scroll = input_x - box_width + 1;
+	input_x = box_width - 1;
+	for (i = 0; i < box_width - 1; i++)
+	    waddch (dialog, instr[scroll + i]);
+    } else
+	waddstr (dialog, instr);
+
+    wmove (dialog, box_y, box_x + input_x);
+
+    wrefresh (dialog);
+
+    while (key != ESC) {
+	key = wgetch (dialog);
+
+	if (button == -1) {	/* Input box selected */
+	    switch (key) {
+	    case TAB:
+	    case KEY_UP:
+	    case KEY_DOWN:
+		break;
+	    case KEY_LEFT:
+		continue;
+	    case KEY_RIGHT:
+		continue;
+	    case KEY_BACKSPACE:
+	    case 127:
+		if (input_x || scroll) {
+		    wattrset (dialog, inputbox_attr);
+		    if (!input_x) {
+			scroll = scroll < box_width - 1 ?
+			    0 : scroll - (box_width - 1);
+			wmove (dialog, box_y, box_x);
+			for (i = 0; i < box_width; i++)
+			    waddch (dialog, instr[scroll + input_x + i] ?
+				    instr[scroll + input_x + i] : ' ');
+			input_x = strlen (instr) - scroll;
+		    } else
+			input_x--;
+		    instr[scroll + input_x] = '\0';
+		    mvwaddch (dialog, box_y, input_x + box_x, ' ');
+		    wmove (dialog, box_y, input_x + box_x);
+		    wrefresh (dialog);
+		}
+		continue;
+	    default:
+		if (key < 0x100 && isprint (key)) {
+		    if (scroll + input_x < MAX_LEN) {
+			wattrset (dialog, inputbox_attr);
+			instr[scroll + input_x] = key;
+			instr[scroll + input_x + 1] = '\0';
+			if (input_x == box_width - 1) {
+			    scroll++;
+			    wmove (dialog, box_y, box_x);
+			    for (i = 0; i < box_width - 1; i++)
+				waddch (dialog, instr[scroll + i]);
+			} else {
+			    wmove (dialog, box_y, input_x++ + box_x);
+			    waddch (dialog, key);
+			}
+			wrefresh (dialog);
+		    } else
+			flash ();	/* Alarm user about overflow */
+		    continue;
+		}
+	    }
+	}
+	switch (key) {
+	case 'O':
+	case 'o':
+	    delwin (dialog);
+	    return 0;
+	case 'H':
+	case 'h':
+	    delwin (dialog);
+	    return 1;
+	case KEY_UP:
+	case KEY_LEFT:
+	    switch (button) {
+	    case -1:
+		button = 1;	/* Indicates "Cancel" button is selected */
+		print_buttons(dialog, height, width, FALSE, TRUE);
+		break;
+	    case 0:
+		button = -1;	/* Indicates input box is selected */
+		print_buttons(dialog, height, width, TRUE, FALSE);
+		wmove (dialog, box_y, box_x + input_x);
+		wrefresh (dialog);
+		break;
+	    case 1:
+		button = 0;	/* Indicates "OK" button is selected */
+		print_buttons(dialog, height, width, TRUE, FALSE);
+		break;
+	    }
+	    break;
+	case TAB:
+	case KEY_DOWN:
+	case KEY_RIGHT:
+	    switch (button) {
+	    case -1:
+		button = 0;	/* Indicates "OK" button is selected */
+		print_buttons(dialog, height, width, TRUE, FALSE);
+		break;
+	    case 0:
+		button = 1;	/* Indicates "Cancel" button is selected */
+		print_buttons(dialog, height, width, FALSE, TRUE);
+		break;
+	    case 1:
+		button = -1;	/* Indicates input box is selected */
+		print_buttons(dialog, height, width, TRUE, FALSE);
+		wmove (dialog, box_y, box_x + input_x);
+		wrefresh (dialog);
+		break;
+	    }
+	    break;
+	case ' ':
+	case '\n':
+	    delwin (dialog);
+	    return (button == -1 ? 0 : button);
+	case 'X':
+	case 'x':
+	    key = ESC;
+	case ESC:
+	    break;
+	}
+    }
+
+    delwin (dialog);
+    return -1;			/* ESC pressed */
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v1.3.59/linux/scripts/lxdialog/lxdialog.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/lxdialog.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,228 @@
+/*
+ *  dialog - Display simple dialog boxes from shell scripts
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+static void Usage (const char *name);
+
+static int separate_output = 0;
+
+typedef int (jumperFn) (const char *title, int argc, const char * const * argv);
+
+struct Mode {
+    char *name;
+    int argmin, argmax, argmod;
+    jumperFn *jumper;
+};
+
+jumperFn j_menu, j_checklist, j_radiolist, j_yesno, j_textbox, j_inputbox;
+jumperFn j_msgbox, j_infobox;
+
+static struct Mode modes[] =
+{
+    {"--menu", 9, 0, 3, j_menu},
+    {"--checklist", 9, 0, 3, j_checklist},
+    {"--radiolist", 9, 0, 3, j_radiolist},
+    {"--yesno",    5,5,1, j_yesno},
+    {"--textbox",  5,5,1, j_textbox},
+    {"--inputbox", 5, 6, 1, j_inputbox},
+    {"--msgbox", 5, 5, 1, j_msgbox},
+    {"--infobox", 5, 5, 1, j_infobox},
+    {NULL, 0, 0, 0, NULL}
+};
+
+static struct Mode *modePtr;
+
+#ifdef LOCALE
+#include <locale.h>
+#endif
+
+int
+main (int argc, const char * const * argv)
+{
+    int offset = 0, clear_screen = 0, end_common_opts = 0, retval;
+    const char *title = NULL;
+
+#ifdef LOCALE
+    (void) setlocale (LC_ALL, "");
+#endif
+
+    if (argc < 2) {
+	Usage (argv[0]);
+	exit (-1);
+    }
+
+    while (offset < argc - 1 && !end_common_opts) {	/* Common options */
+	if (!strcmp (argv[offset + 1], "--title")) {
+	    if (argc - offset < 3 || title != NULL) {
+		Usage (argv[0]);
+		exit (-1);
+	    } else {
+		title = argv[offset + 2];
+		offset += 2;
+	    }
+        } else if (!strcmp (argv[offset + 1], "--backtitle")) {
+            if (backtitle != NULL) {
+                Usage (argv[0]);
+                exit (-1);
+            } else {
+                backtitle = argv[offset + 2];
+                offset += 2;
+            }
+	} else if (!strcmp (argv[offset + 1], "--separate-output")) {
+	    separate_output = 1;
+	    offset++;
+	} else if (!strcmp (argv[offset + 1], "--clear")) {
+	    if (clear_screen) {	/* Hey, "--clear" can't appear twice! */
+		Usage (argv[0]);
+		exit (-1);
+	    } else if (argc == 2) {	/* we only want to clear the screen */
+		init_dialog ();
+		refresh ();	/* init_dialog() will clear the screen for us */
+		end_dialog ();
+		return 0;
+	    } else {
+		clear_screen = 1;
+		offset++;
+	    }
+	} else			/* no more common options */
+	    end_common_opts = 1;
+    }
+
+    if (argc - 1 == offset) {	/* no more options */
+	Usage (argv[0]);
+	exit (-1);
+    }
+    /* use a table to look for the requested mode, to avoid code duplication */
+
+    for (modePtr = modes; modePtr->name; modePtr++)	/* look for the mode */
+	if (!strcmp (argv[offset + 1], modePtr->name))
+	    break;
+
+    if (!modePtr->name)
+	Usage (argv[0]);
+    if (argc - offset < modePtr->argmin)
+	Usage (argv[0]);
+    if (modePtr->argmax && argc - offset > modePtr->argmax)
+	Usage (argv[0]);
+
+
+
+    init_dialog ();
+    retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset);
+
+    if (clear_screen) {		/* clear screen before exit */
+	attr_clear (stdscr, LINES, COLS, screen_attr);
+	refresh ();
+    }
+    end_dialog();
+
+    exit (retval);
+}
+
+/*
+ * Print program usage
+ */
+static void
+Usage (const char *name)
+{
+    fprintf (stderr, "\
+\ndialog, by Savio Lam (lam836@cs.cuhk.hk).\
+\n  patched by Stuart Herbert (S.Herbert@shef.ac.uk)\
+\n  modified/gutted for use as a Linux kernel config tool by \
+\n  William Roadcap (roadcapw@cfw.com)\
+\n\
+\n* Display dialog boxes from shell scripts *\
+\n\
+\nUsage: %s --clear\
+\n       %s [--title <title>] [--separate-output] [--backtitle <backtitle>] --clear <Box options>\
+\n\
+\nBox options:\
+\n\
+\n  --menu      <text> <height> <width> <menu height> <tag1> <item1>...\
+\n  --checklist <text> <height> <width> <list height> <tag1> <item1> <status1>...\
+\n  --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...\
+\n  --textbox   <file> <height> <width>\
+\n  --inputbox  <text> <height> <width> [<init>]\
+\n  --yesno     <text> <height> <width>\
+", name, name);
+    exit (-1);
+}
+
+/*
+ * These are the program jumpers
+ */
+
+int
+j_menu (const char *t, int ac, const char * const * av)
+{
+    return dialog_menu (t, av[2], atoi (av[3]), atoi (av[4]),
+			atoi (av[5]), av[6], (ac - 6) / 2, av + 7);
+}
+
+int
+j_checklist (const char *t, int ac, const char * const * av)
+{
+    return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
+	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK, separate_output);
+}
+
+int
+j_radiolist (const char *t, int ac, const char * const * av)
+{
+    return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
+	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO, separate_output);
+}
+
+int
+j_textbox (const char *t, int ac, const char * const * av)
+{
+    return dialog_textbox (t, av[2], atoi (av[3]), atoi (av[4]));
+}
+
+int
+j_yesno (const char *t, int ac, const char * const * av)
+{
+    return dialog_yesno (t, av[2], atoi (av[3]), atoi (av[4]));
+}
+
+int
+j_inputbox (const char *t, int ac, const char * const * av)
+{
+    int ret = dialog_inputbox (t, av[2], atoi (av[3]), atoi (av[4]),
+                            ac == 6 ? av[5] : (char *) NULL);
+    if (ret == 0)
+        fprintf(stderr, dialog_input_result);
+    return ret;
+}
+
+int
+j_msgbox (const char *t, int ac, const char * const * av)
+{
+    return dialog_msgbox (t, av[2], atoi (av[3]), atoi (av[4]), 1);
+}
+
+int
+j_infobox (const char *t, int ac, const char * const * av)
+{
+    return dialog_msgbox (t, av[2], atoi (av[3]), atoi (av[4]), 0);
+}
+
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/menubox.c linux/scripts/lxdialog/menubox.c
--- v1.3.59/linux/scripts/lxdialog/menubox.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/menubox.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,329 @@
+/*
+ *  menubox.c -- implements the menu box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.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 "dialog.h"
+
+static int menu_width, item_x;
+
+/*
+ * Print menu item
+ */
+static void
+print_item (WINDOW * win, const char *item, int choice, int selected)
+{
+    int j, i;
+
+    j = first_alpha(item);
+
+    /* Clear 'residue' of last item */
+    wattrset (win, menubox_attr);
+    wmove (win, choice, 0);
+    for (i = 0; i < menu_width; i++)
+	waddch (win, ' ');
+    wattrset (win, selected ? item_selected_attr : item_attr);
+    mvwaddstr (win, choice, item_x, (char *)item);
+    wattrset (win, selected ? tag_key_selected_attr : tag_key_attr);
+    mvwaddch(win, choice, item_x+j, item[j]);
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void
+print_arrows (WINDOW * win, int choice, int item_no, int scroll,
+		int y, int x, int height)
+{
+    wmove(win, y, x);
+
+    if (scroll > 0) {
+	wattrset (win, uarrow_attr);
+	waddch (win, ACS_UARROW);
+	waddstr (win, "(-)");
+    }
+    else {
+	wattrset (win, menubox_attr);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+    }
+
+   y = y + height + 1;
+   wmove(win, y, x);
+
+   if ((height < item_no) && (scroll + choice < item_no - 1)) {
+	wattrset (win, darrow_attr);
+	waddch (win, ACS_DARROW);
+	waddstr (win, "(+)");
+    }
+    else {
+	wattrset (win, menubox_border_attr);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+	waddch (win, ACS_HLINE);
+   }
+}
+
+/*
+ * Display the termination buttons.
+ */
+static void
+print_buttons (WINDOW *win, int height, int width,
+		int okval, int exitval, int cancelval)
+{
+    int x = width / 2 - 16;
+    int y = height - 2;
+
+    print_button (win, "Select", y, x, okval);
+    print_button (win, " Exit ", y, x + 12, exitval);
+    print_button (win, " Help ", y, x + 24, cancelval);
+
+    wrefresh (win);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int
+dialog_menu (const char *title, const char *prompt, int height, int width,
+		int menu_height, const char *current, int item_no,
+		const char * const * items)
+
+{
+    int i, j, x, y, cur_x, cur_y, box_x, box_y;
+    int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
+    WINDOW *dialog, *menu;
+
+    max_choice = MIN (menu_height, item_no);
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+	waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    menu_width = width - 6;
+    getyx (dialog, cur_y, cur_x);
+    box_y = height - menu_height - 5;
+    box_x = (width - menu_width) / 2 - 1;
+
+    /* create new window for the menu */
+    menu = subwin (dialog, menu_height, menu_width,
+		y + box_y + 1, x + box_x + 1);
+    keypad (menu, TRUE);
+
+    /* draw a box around the menu items */
+    draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+	      menubox_border_attr, menubox_attr);
+
+    /*
+     * Find length of longest item in order to center menu.
+     * Set 'choice' to default item. 
+     */
+    item_x = 0;
+    for (i = 0; i < item_no; i++) {
+	item_x = MAX (item_x, strlen (items[i * 2 + 1]) + 2);
+	if (strcmp(current, items[i*2]) == 0) choice = i;
+    }
+
+    item_x = (menu_width - item_x) / 2;
+
+    if (choice >= max_choice){
+	scroll = first_item = choice - max_choice + 1;
+	choice = max_choice-1;
+    }
+
+    /* Print the menu */
+    for (i=0; i < max_choice; i++)
+	print_item (menu, items[(first_item + i) * 2 + 1], i, i == choice);
+
+    wnoutrefresh (menu);
+
+    print_arrows(dialog, choice, item_no, scroll,
+		 box_y, box_x+item_x+1, menu_height);
+
+    print_buttons (dialog, height, width, TRUE, FALSE, FALSE);
+
+    while (key != ESC) {
+	key = wgetch (dialog);
+
+        for (i = choice+1; i < max_choice; i++) {
+		j = first_alpha(items[(scroll+i)*2+1]);
+		if (toupper(key) == toupper(items[(scroll+i)*2+1][j]))
+                	break;
+	}
+	if (i == max_choice)
+       		for (i = 0; i < max_choice; i++) {
+			j = first_alpha(items[(scroll+i)*2+1]);
+			if (toupper(key) == toupper(items[(scroll+i)*2+1][j]))
+                		break;
+		}
+
+	if (i < max_choice || key == KEY_UP || key == KEY_DOWN || key == '-' || key == '+') {
+	    if (key == KEY_UP || key == '-') {
+		if (choice == 0) {
+		    if (scroll) {
+
+			/* Scroll menu down */
+			getyx (dialog, cur_y, cur_x);
+
+			if (menu_height > 1) {
+			    /* De-highlight current first item */
+			    print_item (menu, items[scroll * 2 + 1],
+					0, FALSE);
+			    scrollok (menu, TRUE);
+			    wscrl (menu, -1);
+			    scrollok (menu, FALSE);
+			}
+			scroll--;
+			print_item (menu, items[scroll * 2 + 1], 0, TRUE);
+			wnoutrefresh (menu);
+
+    			print_arrows(dialog, choice, item_no, scroll,
+		 			box_y, box_x+item_x+1, menu_height);
+
+			wmove (dialog, cur_y, cur_x);
+		    }
+		    continue;	/* wait for another key press */
+		} else
+		    i = choice - 1;
+	    } else if (key == KEY_DOWN || key == '+')
+		if (choice == max_choice - 1) {
+		    if (scroll + choice < item_no - 1) {
+			/* Scroll menu up */
+			getyx (dialog, cur_y, cur_x);
+			if (menu_height > 1) {
+			    /* De-highlight current last item */
+			    print_item (menu, items[(scroll + max_choice - 1)
+					   * 2 + 1], max_choice - 1, FALSE);
+			    scrollok (menu, TRUE);
+			    scroll (menu);
+			    scrollok (menu, FALSE);
+			}
+			scroll++;
+			print_item (menu, 
+				    items[(scroll + max_choice - 1) * 2 + 1],
+				    max_choice - 1, TRUE);
+			wnoutrefresh (menu);
+
+    			print_arrows(dialog, choice, item_no, scroll,
+		 			box_y, box_x+item_x+1, menu_height);
+
+			wmove (dialog, cur_y, cur_x);
+			wrefresh (dialog);
+		    }
+		    continue;	/* wait for another key press */
+		} else
+		    i = choice + 1;
+
+	    if (i != choice) {
+		/* De-highlight current item */
+		getyx (dialog, cur_y, cur_x);	/* Save cursor position */
+		print_item (menu, items[(scroll + choice) * 2 + 1],
+				choice, FALSE);
+
+		/* Highlight new item */
+		choice = i;
+		print_item (menu, items[(scroll + choice) * 2 + 1],
+				choice, TRUE);
+		wnoutrefresh (menu);
+		wmove (dialog, cur_y, cur_x);
+		wrefresh (dialog);
+	    }
+	    continue;		/* wait for another key press */
+	}
+
+	switch (key) {
+	case TAB:
+	case KEY_LEFT:
+	case KEY_RIGHT:
+	    switch (button) {
+	    case 0:
+		button = 1;
+		print_buttons(dialog, height, width, FALSE, TRUE, FALSE);
+		break;
+	    case 1:
+		button = 2;
+		print_buttons(dialog, height, width, FALSE, FALSE, TRUE);
+		break;
+	    case 2:
+		button = 0;
+		print_buttons(dialog, height, width, TRUE, FALSE, FALSE);
+		break;
+	    }
+	    wrefresh (dialog);
+	    break;
+	case ' ':
+	case 'S':
+	case 's':
+	    delwin (dialog);
+            fprintf(stderr, items[(scroll + choice) * 2]);
+	    return 0;
+	case 'H':
+	case 'h':
+	case '?':
+	    button = 2;
+	case '\n':
+	    delwin (dialog);
+	    if (button == 2) 
+            	fprintf(stderr, "\"%s\" %s", 
+			items[(scroll + choice) * 2 + 1] +
+			first_alpha(items[(scroll + choice) * 2 + 1]),
+			items[(scroll + choice) * 2]);
+	    else
+            	fprintf(stderr, items[(scroll + choice) * 2]);
+
+	    return button;
+	case 'E':
+	case 'e':
+	case 'X':
+	case 'x':
+	    key = ESC;
+	case ESC:
+	    break;
+	}
+    }
+
+    delwin (dialog);
+    return -1;			/* ESC pressed */
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/msgbox.c linux/scripts/lxdialog/msgbox.c
--- v1.3.59/linux/scripts/lxdialog/msgbox.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/msgbox.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,77 @@
+/*
+ *  msgbox.c -- implements the message box and info box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.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 "dialog.h"
+
+/*
+ * Display a message box. Program will pause and display an "OK" button
+ * if the parameter 'pause' is non-zero.
+ */
+int
+dialog_msgbox (const char *title, const char *prompt, int height, int width,
+		int pause)
+{
+    int i, x, y, key = 0;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 2);
+
+    if (pause) {
+	wattrset (dialog, border_attr);
+	mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+	    waddch (dialog, ACS_HLINE);
+	wattrset (dialog, dialog_attr);
+	waddch (dialog, ACS_RTEE);
+
+	print_button (dialog, "  Ok  ",
+		      height - 2, width / 2 - 4, TRUE);
+
+	wrefresh (dialog);
+	while (key != ESC && key != '\n' && key != ' ' &&
+               key != 'O' && key != 'o' && key != 'X' && key != 'x')
+	    key = wgetch (dialog);
+    } else {
+	key = '\n';
+	wrefresh (dialog);
+    }
+
+    delwin (dialog);
+    return key == ESC ? -1 : 0;
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/textbox.c linux/scripts/lxdialog/textbox.c
--- v1.3.59/linux/scripts/lxdialog/textbox.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/textbox.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,537 @@
+/*
+ *  textbox.c -- implements the text box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+static void back_lines (int n);
+static void print_page (WINDOW * win, int height, int width);
+static void print_line (WINDOW * win, int row, int width);
+static char *get_line (void);
+static void print_position (WINDOW * win, int height, int width);
+
+static int hscroll = 0, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached = 0, page_length;
+static char *buf, *page;
+
+/*
+ * Display text from a file in a dialog box.
+ */
+int
+dialog_textbox (const char *title, const char *file, int height, int width)
+{
+    int i, x, y, cur_x, cur_y, fpos, key = 0;
+    int passed_end;
+    char search_term[MAX_LEN + 1];
+    WINDOW *dialog, *text;
+
+    search_term[0] = '\0';	/* no search term entered yet */
+
+    /* Open input file for reading */
+    if ((fd = open (file, O_RDONLY)) == -1) {
+	endwin ();
+	fprintf (stderr,
+		 "\nCan't open input file in dialog_textbox().\n");
+	exit (-1);
+    }
+    /* Get file size. Actually, 'file_size' is the real file size - 1,
+       since it's only the last byte offset from the beginning */
+    if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
+	endwin ();
+	fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
+	exit (-1);
+    }
+    /* Restore file pointer to beginning of file after getting file size */
+    if (lseek (fd, 0, SEEK_SET) == -1) {
+	endwin ();
+	fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
+	exit (-1);
+    }
+    /* Allocate space for read buffer */
+    if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
+	endwin ();
+	fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
+	exit (-1);
+    }
+    if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+	endwin ();
+	fprintf (stderr, "\nError reading file in dialog_textbox().\n");
+	exit (-1);
+    }
+    buf[bytes_read] = '\0';	/* mark end of valid data */
+    page = buf;			/* page is pointer to start of page to be displayed */
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    /* Create window for text region, used for scrolling text */
+    text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
+
+    keypad (text, TRUE);
+
+    /* register the new window, along with its borders */
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+	waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+    print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+    wnoutrefresh (dialog);
+    getyx (dialog, cur_y, cur_x);	/* Save cursor position */
+
+    /* Print first page of text */
+    attr_clear (text, height - 4, width - 2, dialog_attr);
+    print_page (text, height - 4, width - 2);
+    print_position (dialog, height, width);
+    wmove (dialog, cur_y, cur_x);	/* Restore cursor position */
+    wrefresh (dialog);
+
+    while ((key != ESC) && (key != '\n')) {
+	key = wgetch (dialog);
+	switch (key) {
+	case 'E':		/* Exit */
+	case 'e':
+	case 'X':
+	case 'x':
+	    delwin (dialog);
+	    free (buf);
+	    close (fd);
+	    return 0;
+	case 'g':		/* First page */
+	case KEY_HOME:
+	    if (!begin_reached) {
+		begin_reached = 1;
+		/* First page not in buffer? */
+		if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+		    endwin ();
+		    fprintf (stderr,
+		      "\nError moving file pointer in dialog_textbox().\n");
+		    exit (-1);
+		}
+		if (fpos > bytes_read) {	/* Yes, we have to read it in */
+		    if (lseek (fd, 0, SEEK_SET) == -1) {
+			endwin ();
+			fprintf (stderr, "\nError moving file pointer in "
+				 "dialog_textbox().\n");
+			exit (-1);
+		    }
+		    if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+			endwin ();
+			fprintf (stderr,
+			     "\nError reading file in dialog_textbox().\n");
+			exit (-1);
+		    }
+		    buf[bytes_read] = '\0';
+		}
+		page = buf;
+		print_page (text, height - 4, width - 2);
+		print_position (dialog, height, width);
+		wmove (dialog, cur_y, cur_x);	/* Restore cursor position */
+		wrefresh (dialog);
+	    }
+	    break;
+	case 'G':		/* Last page */
+	case KEY_END:
+
+	    end_reached = 1;
+	    /* Last page not in buffer? */
+	    if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+		endwin ();
+		fprintf (stderr,
+		      "\nError moving file pointer in dialog_textbox().\n");
+		exit (-1);
+	    }
+	    if (fpos < file_size) {	/* Yes, we have to read it in */
+		if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
+		    endwin ();
+		    fprintf (stderr,
+		      "\nError moving file pointer in dialog_textbox().\n");
+		    exit (-1);
+		}
+		if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+		    endwin ();
+		    fprintf (stderr,
+			     "\nError reading file in dialog_textbox().\n");
+		    exit (-1);
+		}
+		buf[bytes_read] = '\0';
+	    }
+	    page = buf + bytes_read;
+	    back_lines (height - 4);
+	    print_page (text, height - 4, width - 2);
+	    print_position (dialog, height, width);
+	    wmove (dialog, cur_y, cur_x);	/* Restore cursor position */
+	    wrefresh (dialog);
+	    break;
+	case 'K':		/* Previous line */
+	case 'k':
+	case KEY_UP:
+	    if (!begin_reached) {
+		back_lines (page_length + 1);
+
+		/* We don't call print_page() here but use scrolling to ensure
+		   faster screen update. However, 'end_reached' and
+		   'page_length' should still be updated, and 'page' should
+		   point to start of next page. This is done by calling
+		   get_line() in the following 'for' loop. */
+		scrollok (text, TRUE);
+		wscrl (text, -1);	/* Scroll text region down one line */
+		scrollok (text, FALSE);
+		page_length = 0;
+		passed_end = 0;
+		for (i = 0; i < height - 4; i++) {
+		    if (!i) {
+			/* print first line of page */
+			print_line (text, 0, width - 2);
+			wnoutrefresh (text);
+		    } else
+			/* Called to update 'end_reached' and 'page' */
+			get_line ();
+		    if (!passed_end)
+			page_length++;
+		    if (end_reached && !passed_end)
+			passed_end = 1;
+		}
+
+		print_position (dialog, height, width);
+		wmove (dialog, cur_y, cur_x);	/* Restore cursor position */
+		wrefresh (dialog);
+	    }
+	    break;
+	case 'B':		/* Previous page */
+	case 'b':
+	case KEY_PPAGE:
+	    if (begin_reached)
+		break;
+	    back_lines (page_length + height - 4);
+	    print_page (text, height - 4, width - 2);
+	    print_position (dialog, height, width);
+	    wmove (dialog, cur_y, cur_x);
+	    wrefresh (dialog);
+	    break;
+	case 'J':		/* Next line */
+	case 'j':
+	case KEY_DOWN:
+	    if (!end_reached) {
+		begin_reached = 0;
+		scrollok (text, TRUE);
+		scroll (text);	/* Scroll text region up one line */
+		scrollok (text, FALSE);
+		print_line (text, height - 5, width - 2);
+		wnoutrefresh (text);
+		print_position (dialog, height, width);
+		wmove (dialog, cur_y, cur_x);	/* Restore cursor position */
+		wrefresh (dialog);
+	    }
+	    break;
+	case KEY_NPAGE:		/* Next page */
+	case ' ':
+	    if (end_reached)
+		break;
+
+	    begin_reached = 0;
+	    print_page (text, height - 4, width - 2);
+	    print_position (dialog, height, width);
+	    wmove (dialog, cur_y, cur_x);
+	    wrefresh (dialog);
+	    break;
+	case '0':		/* Beginning of line */
+	case 'H':		/* Scroll left */
+	case 'h':
+	case KEY_LEFT:
+	    if (hscroll <= 0)
+		break;
+
+	    if (key == '0')
+		hscroll = 0;
+	    else
+		hscroll--;
+	    /* Reprint current page to scroll horizontally */
+	    back_lines (page_length);
+	    print_page (text, height - 4, width - 2);
+	    wmove (dialog, cur_y, cur_x);
+	    wrefresh (dialog);
+	    break;
+	case 'L':		/* Scroll right */
+	case 'l':
+	case KEY_RIGHT:
+	    if (hscroll >= MAX_LEN)
+		break;
+	    hscroll++;
+	    /* Reprint current page to scroll horizontally */
+	    back_lines (page_length);
+	    print_page (text, height - 4, width - 2);
+	    wmove (dialog, cur_y, cur_x);
+	    wrefresh (dialog);
+	    break;
+	case ESC:
+	    break;
+	}
+    }
+
+    delwin (dialog);
+    free (buf);
+    close (fd);
+    return -1;			/* ESC pressed */
+}
+
+/*
+ * Go back 'n' lines in text file. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void
+back_lines (int n)
+{
+    int i, fpos;
+
+    begin_reached = 0;
+    /* We have to distinguish between end_reached and !end_reached
+       since at end of file, the line is not ended by a '\n'.
+       The code inside 'if' basically does a '--page' to move one
+       character backward so as to skip '\n' of the previous line */
+    if (!end_reached) {
+	/* Either beginning of buffer or beginning of file reached? */
+	if (page == buf) {
+	    if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+		endwin ();
+		fprintf (stderr, "\nError moving file pointer in "
+			 "back_lines().\n");
+		exit (-1);
+	    }
+	    if (fpos > bytes_read) {	/* Not beginning of file yet */
+		/* We've reached beginning of buffer, but not beginning of
+		   file yet, so read previous part of file into buffer.
+		   Note that we only move backward for BUF_SIZE/2 bytes,
+		   but not BUF_SIZE bytes to avoid re-reading again in
+		   print_page() later */
+		/* Really possible to move backward BUF_SIZE/2 bytes? */
+		if (fpos < BUF_SIZE / 2 + bytes_read) {
+		    /* No, move less then */
+		    if (lseek (fd, 0, SEEK_SET) == -1) {
+			endwin ();
+			fprintf (stderr, "\nError moving file pointer in "
+				 "back_lines().\n");
+			exit (-1);
+		    }
+		    page = buf + fpos - bytes_read;
+		} else {	/* Move backward BUF_SIZE/2 bytes */
+		    if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
+			== -1) {
+			endwin ();
+			fprintf (stderr, "\nError moving file pointer "
+				 "in back_lines().\n");
+			exit (-1);
+		    }
+		    page = buf + BUF_SIZE / 2;
+		}
+		if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+		    endwin ();
+		    fprintf (stderr, "\nError reading file in back_lines().\n");
+		    exit (-1);
+		}
+		buf[bytes_read] = '\0';
+	    } else {		/* Beginning of file reached */
+		begin_reached = 1;
+		return;
+	    }
+	}
+	if (*(--page) != '\n') {	/* '--page' here */
+	    /* Something's wrong... */
+	    endwin ();
+	    fprintf (stderr, "\nInternal error in back_lines().\n");
+	    exit (-1);
+	}
+    }
+    /* Go back 'n' lines */
+    for (i = 0; i < n; i++)
+	do {
+	    if (page == buf) {
+		if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+		    endwin ();
+		    fprintf (stderr,
+			  "\nError moving file pointer in back_lines().\n");
+		    exit (-1);
+		}
+		if (fpos > bytes_read) {
+		    /* Really possible to move backward BUF_SIZE/2 bytes? */
+		    if (fpos < BUF_SIZE / 2 + bytes_read) {
+			/* No, move less then */
+			if (lseek (fd, 0, SEEK_SET) == -1) {
+			    endwin ();
+			    fprintf (stderr, "\nError moving file pointer "
+				     "in back_lines().\n");
+			    exit (-1);
+			}
+			page = buf + fpos - bytes_read;
+		    } else {	/* Move backward BUF_SIZE/2 bytes */
+			if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
+				   SEEK_CUR) == -1) {
+			    endwin ();
+			    fprintf (stderr, "\nError moving file pointer"
+				     " in back_lines().\n");
+			    exit (-1);
+			}
+			page = buf + BUF_SIZE / 2;
+		    }
+		    if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+			endwin ();
+			fprintf (stderr, "\nError reading file in "
+				 "back_lines().\n");
+			exit (-1);
+		    }
+		    buf[bytes_read] = '\0';
+		} else {	/* Beginning of file reached */
+		    begin_reached = 1;
+		    return;
+		}
+	    }
+	} while (*(--page) != '\n');
+    page++;
+}
+
+/*
+ * Print a new page of text. Called by dialog_textbox().
+ */
+static void
+print_page (WINDOW * win, int height, int width)
+{
+    int i, passed_end = 0;
+
+    page_length = 0;
+    for (i = 0; i < height; i++) {
+	print_line (win, i, width);
+	if (!passed_end)
+	    page_length++;
+	if (end_reached && !passed_end)
+	    passed_end = 1;
+    }
+    wnoutrefresh (win);
+}
+
+/*
+ * Print a new line of text. Called by dialog_textbox() and print_page().
+ */
+static void
+print_line (WINDOW * win, int row, int width)
+{
+    int i, y, x;
+    char *line;
+
+    line = get_line ();
+    line += MIN (strlen (line), hscroll);	/* Scroll horizontally */
+    wmove (win, row, 0);	/* move cursor to correct line */
+    waddch (win, ' ');
+    waddnstr (win, line, MIN (strlen (line), width - 2));
+
+    getyx (win, y, x);
+    /* Clear 'residue' of previous line */
+    for (i = 0; i < width - x; i++)
+	waddch (win, ' ');
+}
+
+/*
+ * Return current line of text. Called by dialog_textbox() and print_line().
+ * 'page' should point to start of current line before calling, and will be
+ * updated to point to start of next line.
+ */
+static char *
+get_line (void)
+{
+    int i = 0, fpos;
+    static char line[MAX_LEN + 1];
+
+    end_reached = 0;
+    while (*page != '\n') {
+	if (*page == '\0') {
+	    /* Either end of file or end of buffer reached */
+	    if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+		endwin ();
+		fprintf (stderr, "\nError moving file pointer in "
+			 "get_line().\n");
+		exit (-1);
+	    }
+	    if (fpos < file_size) {	/* Not end of file yet */
+		/* We've reached end of buffer, but not end of file yet,
+		   so read next part of file into buffer */
+		if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+		    endwin ();
+		    fprintf (stderr, "\nError reading file in get_line().\n");
+		    exit (-1);
+		}
+		buf[bytes_read] = '\0';
+		page = buf;
+	    } else {
+		if (!end_reached)
+		    end_reached = 1;
+		break;
+	    }
+	} else if (i < MAX_LEN)
+	    line[i++] = *(page++);
+	else {
+	    /* Truncate lines longer than MAX_LEN characters */
+	    if (i == MAX_LEN)
+		line[i++] = '\0';
+	    page++;
+	}
+    }
+    if (i <= MAX_LEN)
+	line[i] = '\0';
+    if (!end_reached)
+	page++;			/* move pass '\n' */
+
+    return line;
+}
+
+/*
+ * Print current position
+ */
+static void
+print_position (WINDOW * win, int height, int width)
+{
+    int fpos, percent;
+
+    if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+	endwin ();
+	fprintf (stderr, "\nError moving file pointer in print_position().\n");
+	exit (-1);
+    }
+    wattrset (win, position_indicator_attr);
+    percent = !file_size ?
+	100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
+    wmove (win, height - 3, width - 9);
+    wprintw (win, "(%3d%%)", percent);
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/util.c linux/scripts/lxdialog/util.c
--- v1.3.59/linux/scripts/lxdialog/util.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/util.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,339 @@
+/*
+ *  util.c
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+
+/* use colors by default? */
+bool use_colors = 1;
+
+const char *backtitle = NULL;
+
+const char *dialog_result;
+
+/* 
+ * Attribute values, default is for mono display
+ */
+chtype attributes[] =
+{
+    A_NORMAL,			/* screen_attr */
+    A_NORMAL,			/* shadow_attr */
+    A_REVERSE,			/* dialog_attr */
+    A_REVERSE,			/* title_attr */
+    A_REVERSE,			/* border_attr */
+    A_BOLD,			/* button_active_attr */
+    A_DIM,			/* button_inactive_attr */
+    A_UNDERLINE,		/* button_key_active_attr */
+    A_UNDERLINE,		/* button_key_inactive_attr */
+    A_NORMAL,			/* button_label_active_attr */
+    A_NORMAL,			/* button_label_inactive_attr */
+    A_REVERSE,			/* inputbox_attr */
+    A_REVERSE,			/* inputbox_border_attr */
+    A_REVERSE,			/* searchbox_attr */
+    A_REVERSE,			/* searchbox_title_attr */
+    A_REVERSE,			/* searchbox_border_attr */
+    A_REVERSE,			/* position_indicator_attr */
+    A_REVERSE,			/* menubox_attr */
+    A_REVERSE,			/* menubox_border_attr */
+    A_REVERSE,			/* item_attr */
+    A_NORMAL,			/* item_selected_attr */
+    A_REVERSE,			/* tag_attr */
+    A_REVERSE,			/* tag_selected_attr */
+    A_NORMAL,			/* tag_key_attr */
+    A_BOLD,			/* tag_key_selected_attr */
+    A_REVERSE,			/* check_attr */
+    A_REVERSE,			/* check_selected_attr */
+    A_REVERSE,			/* uarrow_attr */
+    A_REVERSE			/* darrow_attr */
+};
+
+
+#include "colors.h"
+
+/*
+ * Table of color values
+ */
+int color_table[][3] =
+{
+    {SCREEN_FG, SCREEN_BG, SCREEN_HL},
+    {SHADOW_FG, SHADOW_BG, SHADOW_HL},
+    {DIALOG_FG, DIALOG_BG, DIALOG_HL},
+    {TITLE_FG, TITLE_BG, TITLE_HL},
+    {BORDER_FG, BORDER_BG, BORDER_HL},
+    {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
+    {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
+    {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
+    {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL},
+    {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL},
+    {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
+     BUTTON_LABEL_INACTIVE_HL},
+    {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
+    {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
+    {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
+    {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
+    {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
+    {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
+    {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
+    {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
+    {ITEM_FG, ITEM_BG, ITEM_HL},
+    {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
+    {TAG_FG, TAG_BG, TAG_HL},
+    {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
+    {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
+    {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
+    {CHECK_FG, CHECK_BG, CHECK_HL},
+    {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
+    {UARROW_FG, UARROW_BG, UARROW_HL},
+    {DARROW_FG, DARROW_BG, DARROW_HL},
+};				/* color_table */
+
+/*
+ * Set window to attribute 'attr'
+ */
+void
+attr_clear (WINDOW * win, int height, int width, chtype attr)
+{
+    int i, j;
+
+    wattrset (win, attr);
+    for (i = 0; i < height; i++) {
+	wmove (win, i, 0);
+	for (j = 0; j < width; j++)
+	    waddch (win, ' ');
+    }
+    touchwin (win);
+}
+
+void dialog_clear (void)
+{
+    attr_clear (stdscr, LINES, COLS, screen_attr);
+    /* Display background title if it exists ... - SLH */
+    if (backtitle != NULL) {
+        int i;
+
+        wattrset (stdscr, screen_attr);
+        mvwaddstr (stdscr, 0, 1, (char *)backtitle);
+        wmove (stdscr, 1, 1);
+        for (i = 1; i < COLS - 1; i++)
+            waddch (stdscr, ACS_HLINE);
+    }
+    wnoutrefresh (stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+void
+init_dialog (void)
+{
+    initscr ();			/* Init curses */
+    keypad (stdscr, TRUE);
+    cbreak ();
+    noecho ();
+
+
+    if (use_colors)	/* Set up colors */
+	color_setup ();
+
+
+    dialog_clear ();
+}
+
+/*
+ * Setup for color display
+ */
+void
+color_setup (void)
+{
+    int i;
+
+    if (has_colors ()) {	/* Terminal supports color? */
+	start_color ();
+
+	/* Initialize color pairs */
+	for (i = 0; i < ATTRIBUTE_COUNT; i++)
+	    init_pair (i + 1, color_table[i][0], color_table[i][1]);
+
+	/* Setup color attributes */
+	for (i = 0; i < ATTRIBUTE_COUNT; i++)
+	    attributes[i] = C_ATTR (color_table[i][2], i + 1);
+    }
+}
+
+/*
+ * End using dialog functions.
+ */
+void
+end_dialog (void)
+{
+    endwin ();
+}
+
+
+/*
+ * Print a string of text in a window, automatically wrap around to the
+ * next line if the string is too long to fit on one line. Note that the
+ * string may contain "\n" to represent a newline character or the real
+ * newline '\n', but in that case, auto wrap around will be disabled.
+ */
+void
+print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x)
+{
+    int first = 1, cur_x, cur_y;
+    int i, prompt_len;
+    char tempstr[MAX_LEN + 1], *word /*,  *tempptr, *tempptr1*/;
+
+    strcpy (tempstr, prompt);
+
+    prompt_len = strlen(tempstr);
+	
+    /*
+     * Remove newlines
+     */
+    for(i=0; i<prompt_len; i++) {
+	if(tempstr[i] == '\n') tempstr[i] = ' ';
+    }
+
+    if (strlen (tempstr) <= width - x * 2) {	/* If prompt is short */
+	wmove (win, y, (width - strlen (tempstr)) / 2);
+	waddstr (win, tempstr);
+    } else {
+	cur_x = x;
+	cur_y = y;
+	/* Print prompt word by word, wrap around if necessary */
+	while ((word = strtok (first ? tempstr : NULL, " ")) != NULL) {
+	    if (first)		/* First iteration */
+		first = 0;
+	    if (cur_x + strlen (word) > width) {
+		cur_y++;	/* wrap to next line */
+		cur_x = x;
+	    }
+	    wmove (win, cur_y, cur_x);
+	    waddstr (win, word);
+	    getyx (win, cur_y, cur_x);
+	    cur_x++;
+	}
+    }
+}
+
+/*
+ * Print a button
+ */
+void
+print_button (WINDOW * win, const char *label, int y, int x, int selected)
+{
+    int i, temp;
+
+    wmove (win, y, x);
+    wattrset (win, selected ? button_active_attr : button_inactive_attr);
+    waddstr (win, "<");
+    temp = strspn (label, " ");
+    label += temp;
+    wattrset (win, selected ? button_label_active_attr
+	      : button_label_inactive_attr);
+    for (i = 0; i < temp; i++)
+	waddch (win, ' ');
+    wattrset (win, selected ? button_key_active_attr
+	      : button_key_inactive_attr);
+    waddch (win, label[0]);
+    wattrset (win, selected ? button_label_active_attr
+	      : button_label_inactive_attr);
+    waddstr (win, (char *)label + 1);
+    wattrset (win, selected ? button_active_attr : button_inactive_attr);
+    waddstr (win, ">");
+    wmove (win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box (WINDOW * win, int y, int x, int height, int width,
+	  chtype box, chtype border)
+{
+    int i, j;
+
+    wattrset (win, 0);
+    for (i = 0; i < height; i++) {
+	wmove (win, y + i, x);
+	for (j = 0; j < width; j++)
+	    if (!i && !j)
+		waddch (win, border | ACS_ULCORNER);
+	    else if (i == height - 1 && !j)
+		waddch (win, border | ACS_LLCORNER);
+	    else if (!i && j == width - 1)
+		waddch (win, box | ACS_URCORNER);
+	    else if (i == height - 1 && j == width - 1)
+		waddch (win, box | ACS_LRCORNER);
+	    else if (!i)
+		waddch (win, border | ACS_HLINE);
+	    else if (i == height - 1)
+		waddch (win, box | ACS_HLINE);
+	    else if (!j)
+		waddch (win, border | ACS_VLINE);
+	    else if (j == width - 1)
+		waddch (win, box | ACS_VLINE);
+	    else
+		waddch (win, box | ' ');
+    }
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void
+draw_shadow (WINDOW * win, int y, int x, int height, int width)
+{
+    int i;
+
+    if (has_colors ()) {	/* Whether terminal supports color? */
+	wattrset (win, shadow_attr);
+	wmove (win, y + height, x + 2);
+	for (i = 0; i < width; i++)
+	    waddch (win, winch (win) & A_CHARTEXT);
+	for (i = y + 1; i < y + height + 1; i++) {
+	    wmove (win, i, x + width);
+	    waddch (win, winch (win) & A_CHARTEXT);
+	    waddch (win, winch (win) & A_CHARTEXT);
+	}
+	wnoutrefresh (win);
+    }
+}
+
+/*
+ *  Return the position of the first alphabetic character in a string.
+ */
+int
+first_alpha(const char *string)
+{
+	int i, in_paren=0;
+
+	for (i = 0; i < strlen(string); i++) {
+		if (string[i] == '(') ++in_paren;
+		if (string[i] == ')') --in_paren;
+
+		if ((! in_paren) && isalpha(string[i]))
+			return i;
+	}
+
+	return 0;
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/lxdialog/yesno.c linux/scripts/lxdialog/yesno.c
--- v1.3.59/linux/scripts/lxdialog/yesno.c	Thu Jan  1 02:00:00 1970
+++ linux/scripts/lxdialog/yesno.c	Wed Feb  7 09:47:28 1996
@@ -0,0 +1,114 @@
+/*
+ *  yesno.c -- implements the yes/no box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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 "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void
+print_buttons(WINDOW *dialog, int height, int width, int okval, int cancelval)
+{
+    int x = width / 2 - 10;
+    int y = height - 2;
+
+    print_button (dialog, " Yes ", y, x, okval);
+    print_button (dialog, "  No  ", y, x + 13, cancelval);
+
+    wrefresh (dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int
+dialog_yesno (const char *title, const char *prompt, int height, int width)
+{
+    int i, x, y, key = 0, button = 0;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+	waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL) {
+	wattrset (dialog, title_attr);
+	mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+	waddstr (dialog, (char *)title);
+	waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    print_buttons(dialog, height, width, TRUE, FALSE);
+
+    while (key != ESC) {
+	key = wgetch (dialog);
+	switch (key) {
+	case 'Y':
+	case 'y':
+	    delwin (dialog);
+	    return 0;
+	case 'N':
+	case 'n':
+	    delwin (dialog);
+	    return 1;
+
+	case TAB:
+	case KEY_UP:
+	case KEY_DOWN:
+	case KEY_LEFT:
+	case KEY_RIGHT:
+	    if (!button) {
+		button = 1;	/* "No" button selected */
+    		print_buttons(dialog, height, width, FALSE, TRUE);
+	    } else {
+		button = 0;	/* "Yes" button selected */
+    		print_buttons(dialog, height, width, TRUE, FALSE);
+	    }
+	    wrefresh (dialog);
+	    break;
+	case ' ':
+	case '\n':
+	    delwin (dialog);
+	    return button;
+	case ESC:
+	    break;
+	}
+    }
+
+    delwin (dialog);
+    return -1;			/* ESC pressed */
+}
diff -u --recursive --new-file v1.3.59/linux/scripts/patch-kernel linux/scripts/patch-kernel
--- v1.3.59/linux/scripts/patch-kernel	Thu Jan  4 21:55:00 1996
+++ linux/scripts/patch-kernel	Thu Jan 25 20:54:42 1996
@@ -36,7 +36,8 @@
     fi
 
     echo "Applying $patch..."
-    gunzip -dc $patchdir/$patch | patch -p1 -s -E -f -d $sourcedir
+    gunzip -dc $patchdir/$patch | patch -p1 -s -E -d $sourcedir
+# used to be patch -p1 -s -E -f -d $sourcedir
     if [ "`find $sourcedir -name \*.rej -print`" ]
     then
         echo "Aborting.  Reject files found."
diff -u --recursive --new-file v1.3.59/linux/scripts/tail.tk linux/scripts/tail.tk
--- v1.3.59/linux/scripts/tail.tk	Thu Jan  4 21:55:00 1996
+++ linux/scripts/tail.tk	Wed Feb  7 08:56:11 1996
@@ -33,9 +33,11 @@
 update_mainmenu .f0
 
 button .f0_bot.r.save -text "Save and Exit" -width 25 -command {
-	writeconfig .config include/linux/autoconf.h; wrapup .wrap }
+	writeconfig .config include/linux/autoconf.h; wrapup .wrap } \
+	-activebackground green
 
-button .f0_bot.r.quit -text "Quit Without Saving" -command { maybe_exit .maybe } -width 25 \
+button .f0_bot.r.quit -text "Quit Without Saving" -width 25 \
+	-command { maybe_exit .maybe } \
 	-activebackground red -activeforeground white
 
 button .f0_bot.l.store -text "Store Configuration to File" -width 25 -command {
diff -u --recursive --new-file v1.3.59/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.59/linux/scripts/tkgen.c	Thu Jan  4 21:55:00 1996
+++ linux/scripts/tkgen.c	Wed Feb  7 12:26:27 1996
@@ -4,8 +4,10 @@
  * Eric Youngdale
  * 10/95
  *
- * 1995 01 04 - Aesthetic improvements by Avery Pennarun
+ * 1996 01 04 - Avery Pennarun - Aesthetic improvements
  *              <apenwarr@foxnet.net>
+ *
+ * 1996 01 24 - Avery Pennarun - Bugfixes and more aesthetics
  */
 #include <stdio.h>
 #include "tkparse.h"
@@ -44,7 +46,27 @@
   printf("\tmessage $w.m -width 400 -aspect 300 -background grey -text \\\n");
   printf("\t\t\"%s\"  -relief raised -bg grey\n",label);
   printf("\tpack $w.m -pady 10 -side top -padx 10\n");
-  printf("\twm title $w \"%s\" \n\n\n", label);
+  printf("\twm title $w \"%s\" \n\n", label);
+  
+  printf("\tframe $w.topline -relief ridge -borderwidth 2 -height 2\n");
+  printf("\tpack $w.topline -side top -fill x\n\n");
+  
+  printf("\tframe $w.config\n");
+  printf("\tpack $w.config -fill y -expand on\n\n");
+  
+  printf("\tscrollbar $w.config.vscroll -command \"$w.config.canvas yview\"\n");
+  printf("\tpack $w.config.vscroll -side right -fill y\n\n");
+  
+  printf("\tframe $w.botline -relief ridge -borderwidth 2 -height 2\n");
+  printf("\tpack $w.botline -side top -fill x\n\n");
+  
+  printf("\tcanvas $w.config.canvas -height 1\\\n"
+  	 "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n"
+  	 "\t\t-width [expr [winfo screenwidth .] * 1 / 2] \n");
+  printf("\tframe $w.config.f\n");
+  printf("\tpack $w.config.canvas -side right -fill y\n");
+  
+  printf("\n\n");
 }
 
 /*
@@ -174,11 +196,11 @@
       break;
     case tok_int:
       printf("} then { ");
-      printf(".menu%d.x%d.x configure -state normal; ", menu_num, line_num);
-      printf(".menu%d.x%d.l configure -state normal; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.x configure -state normal -fore black; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
       printf("} else { ");
-      printf(".menu%d.x%d.x configure -state disabled;", menu_num, line_num );
-      printf(".menu%d.x%d.l configure -state disabled;", menu_num, line_num );
+      printf(".menu%d.config.f.x%d.x configure -state disabled -fore gray60;", menu_num, line_num );
+      printf(".menu%d.config.f.x%d.l configure -state disabled;", menu_num, line_num );
       printf("}\n");
       break;
     case tok_bool:
@@ -186,7 +208,7 @@
       /*
        * If a bool is just a button, then use this definition.
        */
-      printf("} then { .menu%d.x%d configure -state normal } else { .menu%d.x%d configure -state disabled }\n",
+      printf("} then { .menu%d.config.f.x%d configure -state normal } else { .menu%d.config.f.x%d configure -state disabled }\n",
 	     menu_num, line_num,
 	     menu_num, line_num );
 #else
@@ -194,14 +216,14 @@
        * If a bool is a radiobutton, then use this instead.
        */
       printf("} then { ");
-      printf(".menu%d.x%d.y configure -state normal;",menu_num, line_num);
-      printf(".menu%d.x%d.n configure -state normal;",menu_num, line_num);
-      printf(".menu%d.x%d.l configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num);
       printf("set %s [expr $%s&15];", item->optionname, item->optionname);
       printf("} else { ");
-      printf(".menu%d.x%d.y configure -state disabled;",menu_num, line_num);
-      printf(".menu%d.x%d.n configure -state disabled;",menu_num, line_num);
-      printf(".menu%d.x%d.l configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num);
       printf("set %s [expr $%s|16];", item->optionname, item->optionname);
       printf("}\n");
 #endif
@@ -211,30 +233,31 @@
       printf("} then { ");
       if( item->tok == tok_dep_tristate )
 	{
+	  printf("global %s;", item->depend.str);
 	  printf("if { $%s == 2 } then {", item->depend.str);
-	  printf(".menu%d.x%d.y configure -state disabled;",menu_num, line_num);
+	  printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
 	  printf("} else {");
-	  printf(".menu%d.x%d.y configure -state normal;",menu_num, line_num);
+	  printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
 	  printf("}; ");
 	}
       else
 	{
-	  printf(".menu%d.x%d.y configure -state normal;",menu_num, line_num);
+	  printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
 	}
       
-      printf(".menu%d.x%d.n configure -state normal;",menu_num, line_num);
-      printf(".menu%d.x%d.m configure -state normal;",menu_num, line_num);
-      printf(".menu%d.x%d.l configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.m configure -state normal;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num);
       /*
        * Or in a bit to the variable - this causes all of the radiobuttons
        * to be deselected (i.e. not be red).
        */
       printf("set %s [expr $%s&15];", item->optionname, item->optionname);
       printf("} else { ");
-      printf(".menu%d.x%d.y configure -state disabled;",menu_num, line_num);
-      printf(".menu%d.x%d.n configure -state disabled;",menu_num, line_num);
-      printf(".menu%d.x%d.m configure -state disabled;",menu_num, line_num);
-      printf(".menu%d.x%d.l configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.m configure -state disabled;",menu_num, line_num);
+      printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num);
       /*
        * Clear the disable bit - this causes the correct radiobutton
        * to appear selected (i.e. turn red).
@@ -403,12 +426,45 @@
   printf("\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
 
   printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n");
-  printf("\tpack $w.f -pady 10 -side top -anchor w -fill x -expand on\n");
+  printf("\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n");
   printf("\tfocus $w\n");
-  printf("\tupdate_menu%d $w\n", menu_num);
+  printf("\tupdate_menu%d $w.config.f\n", menu_num);
   printf("\tglobal winx; global winy\n");
   printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n");
   printf("\twm geometry $w +$winx+$winy\n");
+  printf("\twm resizable $w no yes\n\n");
+  
+  /*
+   * Now that the whole window is in place, we need to wait for an "update"
+   * so we can tell the canvas what its virtual size should be.
+   *
+   * Unfortunately, this causes some ugly screen-flashing because the whole
+   * window is drawn, and then it is immediately resized.  It seems
+   * unavoidable, though, since "frame" objects won't tell us their size
+   * until after an update, and "canvas" objects can't automatically pack
+   * around frames.  Sigh.
+   */
+  printf("\tupdate idletasks\n");
+  printf("\t$w.config.canvas create window 0 0 -anchor nw -window $w.config.f\n\n");
+  printf("\t$w.config.canvas configure \\\n"
+  	 "\t\t-width [expr [winfo reqwidth $w.config.f] + 1]\\\n"
+  	 "\t\t-scrollregion \"-1 -1 [expr [winfo reqwidth $w.config.f] + 1] \\\n"
+  	 "\t\t\t [expr [winfo reqheight $w.config.f] + 1]\"\n\n");
+  	 
+  /*
+   * If the whole canvas will fit in 3/4 of the screen height, do it;
+   * otherwise, resize to around 1/2 the screen and let us scroll.
+   */
+  printf("\tset winy [expr [winfo reqh $w] - [winfo reqh $w.config.canvas]]\n");
+  printf("\tset scry [expr [winfo screenh $w] / 2]\n");
+  printf("\tset maxy [expr [winfo screenh $w] * 3 / 4]\n");
+  printf("\tset canvtotal [expr [winfo reqh $w.config.f] + 2]\n");
+  printf("\tif [expr $winy + $canvtotal < $maxy] {\n"
+  	 "\t\t$w.config.canvas configure -height $canvtotal\n"
+  	 "\t} else {\n"
+  	 "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n"
+  	 "\t}\n");
+  
   printf("}\n\n\n");
 
   /*
@@ -460,7 +516,8 @@
 	   */
 	  if(cfg->tok == tok_dep_tristate)
 	    {
-	      printf("\tif {$%s == 2 } then { .menu3.x5.y configure -state normal} else { .menu3.x5.y configure -state disabled}\n",cfg->depend.str,
+	      printf("\tglobal %s;", cfg->depend.str);
+	      printf("\tif {$%s == 2 } then { .menu%d.config.f.x%d.y configure -state disabled } else { .menu%d.config.f.x%d.y configure -state normal}\n",cfg->depend.str,
 		     menu_num, cfg->menu_line,
 		     menu_num, cfg->menu_line);
 	      
@@ -511,6 +568,7 @@
       }
   }
 
+#ifdef OLD_SPLIT_MENUS
   /*
    * Now figure out how many items go on each page.
    */
@@ -518,6 +576,10 @@
   while(tot / div > 15) div++;
   *menu_max = cfg->menu_number + div - 1;
   *menu_maxlines = (tot + div -1) / div;
+#else
+  *menu_max = cfg->menu_number;
+  *menu_maxlines = tot;
+#endif
 }
 
 /*
@@ -633,7 +695,7 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
-	  printf("\tbool $w %d %d \"%s\" %s\n",
+	  printf("\tbool $w.config.f %d %d \"%s\" %s\n",
 		 cfg->menu_number,
 		 cfg->menu_line,
 		 cfg->label,
@@ -641,12 +703,12 @@
 	  break;
 
 	case tok_choice:
-	  printf("\t$w.x%d.x.menu add radiobutton -label \"%s\" -variable %s -value %d -command \"update_menu%d .menu%d\"\n",
+	  printf("\t$w.config.f.x%d.x.menu add radiobutton -label \"%s\" -variable %s -value %d -command \"update_menu%d .menu%d.config.f\"\n",
 		 cfg1->menu_line,
 		 cfg->label,
 		 cfg1->optionname,
 		 cfg->choice_value,
-		 cfg->menu_number, cfg->menu_number);
+		 cfg1->menu_number, cfg1->menu_number);
 	  break;
 	case tok_choose:
 	  if( cfg->menu_number != menu_num )
@@ -656,18 +718,18 @@
 	      menu_num = cfg->menu_number;
 	    }
 #if 0
-	  printf("\tmenubutton $w.line%d -text \"%s\" -menu $w.line%d.menu \\\n",
+	  printf("\tmenubutton $w.config.f.line%d -text \"%s\" -menu $w.config.f.line%d.menu \\\n",
 		 cfg->menu_line, cfg->label, cfg->menu_line);
 	  printf("\t	-relief raised -width 35\n");
-	  printf("\tpack $w.line%d -anchor w\n", cfg->menu_line);
-	  printf("\tmenu $w.line%d.menu\n", cfg->menu_line);
+	  printf("\tpack $w.config.f.line%d -anchor w\n", cfg->menu_line);
+	  printf("\tmenu $w.config.f.line%d.menu\n", cfg->menu_line);
 #else
-	  printf("\tminimenu $w %d %d \"%s\" %s\n",
+	  printf("\tminimenu $w.config.f %d %d \"%s\" %s\n",
 	  	cfg->menu_number,
 	  	cfg->menu_line,
 	  	cfg->label,
 	  	cfg->optionname);
-	  printf("\tmenu $w.x%d.x.menu\n", cfg->menu_line);
+	  printf("\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line);
 #endif
 	  cfg1 = cfg;
 	  break;
@@ -678,7 +740,7 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
-	  printf("\ttristate $w %d %d \"%s\" %s\n",
+	  printf("\ttristate $w.config.f %d %d \"%s\" %s\n",
 		 cfg->menu_number,
 		 cfg->menu_line,
 		 cfg->label,
@@ -691,7 +753,7 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
-	  printf("\tdep_tristate $w %d %d \"%s\" %s\n",
+	  printf("\tdep_tristate $w.config.f %d %d \"%s\" %s\n",
 		 cfg->menu_number,
 		 cfg->menu_line,
 		 cfg->label,
@@ -705,7 +767,7 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
-	  printf("\tint $w %d %d \"%s\" %s\n",
+	  printf("\tint $w.config.f %d %d \"%s\" %s\n",
 		 cfg->menu_number,
 		 cfg->menu_line,
 		 cfg->label,
@@ -718,7 +780,7 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
-	  printf("\tdo_sound $w %d %d\n",
+	  printf("\tdo_sound $w.config.f %d %d\n",
 		 cfg->menu_number,
 		 cfg->menu_line);
 	  break;
@@ -743,9 +805,9 @@
   printf("\tdo_make -C drivers/sound config\n");
   printf("\techo check_sound_config %d\n",menu_num);
 #endif
-  printf("\tlabel $w.m0 -bitmap error\n");
-  printf("\tmessage $w.m1 -width 400 -aspect 300 -text \"The sound drivers cannot as of yet be configured via the X-based interface\" -relief raised\n");
-  printf("\tpack $w.m0 $w.m1 -side top -pady 10 -expand on\n");
+  printf("\tlabel $w.config.f.m0 -bitmap error\n");
+  printf("\tmessage $w.config.f.m1 -width 400 -aspect 300 -text \"The sound drivers cannot as of yet be configured via the X-based interface\" -relief raised\n");
+  printf("\tpack $w.config.f.m0 $w.config.f.m1 -side top -pady 10 -expand on\n");
   /*
    * Close out the last menu.
    */
@@ -856,11 +918,16 @@
 	    {
 	      if(cfg->tok == tok_dep_tristate)
 		{
-		  printf("\tif {$%s == 2 } then { write_variable $cfg $autocfg %s $%s %s } else { write_variable $cfg $autocfg %s $notset $notmod }\n",
+		  printf("\tif {$%s == 2 } then {\n"
+		  	 "\t\twrite_variable $cfg $autocfg %s $notset $notmod\n"
+		  	 "\t} else {\n"
+		  	 "\t\twrite_variable $cfg $autocfg %s $%s %s\n"
+		  	 "\t}\n",
+		  	 cfg->depend.str,
+			 cfg->optionname,
 			 cfg->optionname,
 			 cfg->optionname,
-			 cfg->depend.str,
-			 cfg->optionname);
+			 cfg->depend.str);
 		}
 	      else if(cfg->tok == tok_comment)
 		{
