Linux 0.99 patchlevel 14

Linux 0.99 patchlevel 14 is available on nic.funet.fi in the usual place
(pub/OS/Linux/PEOPLE/Linus).  There are no diffs relative to pl13, as
too much has changed (the directory structure changed and the sound
driver was added).  Diffs relative to the last ALPHA version (13t) are
in the "pl13-ALPHA's" subdirectory along with the actual ALPHA versions.

The changes to pl13t are rather minor: most of them are just more printf
format fixes to make gcc-2.5.x happy (Chip Salzenberg).  Only one very
minor bugfix which made pl13t not notice the WP bit on a 486.

It would seem to be a good idea to use gcc-2.5.x to compile the kernel,
as that seems to fix at least one known bug in earlier gcc versions.  I
hope that pl14 will be even more stable than pl13 has turned out to be,
and especially the networking code seems to have become much more
dependable.  Thanks Alan & co.

Changes to the last official release (p13) are too numerous to mention
(or even to remember), but they include NTP support, updated SCSI and
networking drivers, >16MB swap area handling, added sound support,
read-only HPFS filesystem, memory management cleanups (especially
cleaned up mmap() some more).  Also, pl14 contains updated ext2fs code,
along with minor fixes (especially concerning the time values) in other
filesystems, and fixed unnamed/named pipe select() semantics.

The reorganizations include moving all device drivers to a subdirectory
of their own (linux/drivers), centralizing the major number handling
(<linux/major.h> etc...  Possibly cleaner and/or easier to keep track of
different drivers.  Finally, the first 4kB of physical memory is no
longer cleared on bootup: tytso reports that this feature now enables
some portables to use the power-saving features under linux.  This could
also be useful for the DOS emulator to check where the interrupt
pointers pointed at startup.

			Linus
diff --git a/CHANGES b/CHANGES
index dde6c32..4e74e16 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,16 @@
+CHANGES since 0.99 patchlevel 13:
+
+ - new kernel source layout: drivers separated
+ - lots of networking bugs fixed, and new network card drivers (Alan Cox,
+   Donald Becker &co)
+ - sound driver added to the default source distribution (Hannu
+   Savolainen)
+ - updated SCSI driver code (Eric Youngdale, Drew Eckhardt &co)
+ - readonly OS/2 filesystem support (HPFS) added (Chris Smith)
+ - NTP support (Philip Gladstone, Torsten Duwe, ??)
+ - fixed 16MB swap-area limit
+ - lots of minor cleanups, buxfixes etc.
+
 CHANGES since 0.99 patchlevel 12 and earlier:
 
  - the bad memory management one-liner bug in pl12 is naturally fixed.
diff --git a/Configure b/Configure
index 13197bb..77c8445 100644
--- a/Configure
+++ b/Configure
@@ -202,6 +202,7 @@
 		:)	[ "$branch" = "t" ] && echo "$raw_input_line" ;;
 		int)	[ "$branch" = "t" ] && int "$rest" ;;
 		bool)	[ "$branch" = "t" ] && bool "$rest" ;;
+		exec)	[ "$branch" = "t" ] && ( sh -c "$rest" ) ;;
 		if)	stack="$branch $stack"
 			if [ "$branch" = "t" ] && eval "$rest"; then
 				branch=t
diff --git a/Makefile b/Makefile
index fdf817c..451b050 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 0.99
-PATCHLEVEL = 13
-ALPHA = k
+PATCHLEVEL = 14
+ALPHA =
 
 all:	Version zImage
 
@@ -44,8 +44,7 @@
 # The number is the same as you would ordinarily press at bootup.
 #
 
-# SVGA_MODE=	-DSVGA_MODE=3
-SVGA_MODE=	-DSVGA_MODE=NORMAL_VGA
+SVGA_MODE=	-DSVGA_MODE=3
 
 # Special options.
 #OPTS	= -pro
@@ -58,6 +57,8 @@
 
 ifdef CONFIG_M486
 CFLAGS := $(CFLAGS) -m486
+else
+CFLAGS := $(CFLAGS) -m386
 endif
 
 #
@@ -114,14 +115,13 @@
 
 config:
 	$(CONFIG_SHELL) Configure $(OPTS) < config.in
+	@if grep -s '^CONFIG_SOUND' .config~ ; then \
+		$(MAKE) -C drivers/sound config; \
+		else : ; fi
 	mv .config~ .config
-	$(MAKE) soundconf
-
-soundconf:
-	cd drivers/sound;$(MAKE) config
 
 linuxsubdirs: dummy
-	@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
 
 tools/./version.h: tools/version.h
 
@@ -149,7 +149,7 @@
 	$(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<
 
 tools/system:	boot/head.o init/main.o tools/version.o linuxsubdirs
-	$(LD) $(LDFLAGS) -M boot/head.o init/main.o tools/version.o \
+	$(LD) $(LDFLAGS) -T 1000 -M boot/head.o init/main.o tools/version.o \
 		$(ARCHIVES) \
 		$(FILESYSTEMS) \
 		$(DRIVERS) \
@@ -171,7 +171,7 @@
 	$(LD86) -s -o boot/bootsect boot/bootsect.o
 
 zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
-	cd zBoot;$(MAKE)
+	$(MAKE) -C zBoot
 
 zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
 	tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
@@ -209,16 +209,21 @@
 drivers: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=drivers
 
+net: dummy
+	$(MAKE) linuxsubdirs SUBDIRS=net
+
 clean:
 	rm -f core `find . -name '*.[oas]' -print`
+	rm -f core `find . -name 'core' -print`
 	rm -f zImage zSystem.map tools/zSystem tools/system
-	rm -f Image System.map boot/bootsect boot/setup \
-		boot/bootsect.s boot/setup.s boot/head.s init/main.s
+	rm -f Image System.map boot/bootsect boot/setup
+	rm -f zBoot/zSystem zBoot/xtract zBoot/piggyback
+	rm -f drivers/sound/configure
 	rm -f init/*.o tools/build boot/*.o tools/*.o
-	for i in zBoot $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
 
 mrproper: clean
 	rm -f include/linux/autoconf.h tools/version.h
+	rm -f drivers/sound/local.h
 	rm -f .version .config* config.old
 	rm -f .depend `find . -name .depend -print`
 
@@ -232,7 +237,7 @@
 	touch tools/version.h
 	for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done > .depend~
 	for i in tools/*.c;do echo -n "tools/";$(CPP) -M $$i;done >> .depend~
-	for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
 	rm -f tools/version.h
 	mv .depend~ .depend
 
diff --git a/boot/bootsect.S b/boot/bootsect.S
index 610acac..261d5a1 100644
--- a/boot/bootsect.S
+++ b/boot/bootsect.S
@@ -1,7 +1,7 @@
 !
 ! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
-! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
-! versions of linux
+! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current
+! versions of linux which compress the kernel
 !
 #include <linux/config.h>
 SYSSIZE = DEF_SYSSIZE
@@ -19,10 +19,11 @@
 ! It then loads 'setup' directly after itself (0x90200), and the system
 ! at 0x10000, using BIOS interrupts. 
 !
-! NOTE! currently system is at most 8*65536 bytes long. This should be no
-! problem, even in the future. I want to keep it simple. This 512 kB
+! NOTE! currently system is at most (8*65536-4096) bytes long. This should 
+! be no problem, even in the future. I want to keep it simple. This 508 kB
 ! kernel size should be enough, especially as this doesn't contain the
-! buffer cache as in minix
+! buffer cache as in minix (and especially now that the kernel is 
+! compressed :-)
 !
 ! The loader has been made as simple as possible, and continuos
 ! read errors will result in a unbreakable loop. Reboot by hand. It
diff --git a/boot/head.S b/boot/head.S
index 322f198..3caf93f 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -25,7 +25,8 @@
 #define CL_OFFSET	0x90022
 
 /*
- * swapper_pg_dir is the main page directory, address 0x00001000
+ * swapper_pg_dir is the main page directory, address 0x00001000 (or at
+ * address 0x00101000 for a compressed boot).
  */
 startup_32:
 	cld
@@ -237,6 +238,12 @@
 /*
  * page 0 is made non-existent, so that kernel NULL pointer references get
  * caught. Thus the swapper page directory has been moved to 0x1000
+ *
+ * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
+ * with the introduction of the compressed boot code.  Theoretically,
+ * the original design of overlaying the startup code with the swapper
+ * page directory is still possible --- it would reduce the size of the kernel
+ * by 2-3k.  This would be a good thing to do at some point.....
  */
 .org 0x1000
 _swapper_pg_dir:
diff --git a/boot/setup.S b/boot/setup.S
index c775ead..2ba710f 100644
--- a/boot/setup.S
+++ b/boot/setup.S
@@ -152,19 +152,21 @@
 	mov	al,#0x80	! disable NMI for the bootup sequence
 	out	#0x70,al
 
-! first we move the system to it's rightful place
+! first we move the system to its rightful place
 
-	xor	ax,ax		! clear ax
+	mov	ax,#0x100	! start of destination segment
+	mov	bx,#0x1000	! start of source segment
 	cld			! 'direction'=0, movs moves forward
 do_move:
 	mov	es,ax		! destination segment
-	add	ax,#0x1000
+	add	ax,#0x100
 	cmp	ax,#0x9000
 	jz	end_move
-	mov	ds,ax		! source segment
+	mov	ds,bx		! source segment
+	add	bx,#0x100
 	sub	di,di
 	sub	si,si
-	mov 	cx,#0x8000
+	mov 	cx,#0x800
 	rep
 	movsw
 	jmp	do_move
@@ -248,7 +250,7 @@
 	lmsw	ax		! This is it!
 	jmp	flush_instr
 flush_instr:
-	jmpi	0,KERNEL_CS	! jmp offset 0 of segment 0x10 (cs)
+	jmpi	0x1000,KERNEL_CS	! jmp offset 1000 of segment 0x10 (cs)
 
 ! This routine checks that the keyboard command queue is empty
 ! (after emptying the output buffers)
@@ -392,7 +394,17 @@
 	mov	ax,#0x501c	! return 80x28
 	ret
 /* svga modes */
-svga:	cld	
+svga:   cld
+        lea     si,id9GXE	! Check for the #9GXE (jyanowit@orixa.mtholyoke.edu,thanks dlm40629@uxa.cso.uiuc.edu)
+        mov     di,#0x49	! id string is at c000:049
+        mov     cx,#0x11	! length of "Graphics Power By"
+        repe
+        cmpsb
+        jne     of1280
+is9GXE:	lea 	si,dsc9GXE	! table of descriptions of video modes for BIOS
+	lea	di,mo9GXE	! table of sizes of video modes for my BIOS
+	br	selmod		! go ask for video mode
+of1280:	cld	
 	lea	si,idf1280	! Check for Orchid F1280 (dingbat@diku.dk)
 	mov	di,#0x10a	! id string is at c000:010a
 	mov	cx,#0x21	! length
@@ -806,6 +818,7 @@
 idparadise:	.ascii	"VGA="
 idoakvga:	.ascii  "OAK VGA "
 idf1280:	.ascii	"Orchid Technology Fahrenheit 1280"
+id9GXE:		.ascii  "Graphics Power By"
 idVRAM:		.ascii	"Stealth VRAM"
 
 ! Manufacturer:	  Numofmodes+2:	Mode:
@@ -823,6 +836,7 @@
 motseng:	.byte	0x07,	0x26, 0x2a, 0x23, 0x24, 0x22
 movideo7:	.byte	0x08,	0x40, 0x43, 0x44, 0x41, 0x42, 0x45
 mooakvga:	.byte   0x08,   0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
+mo9GXE:		.byte	0x04,	0x54, 0x55
 mof1280:	.byte	0x04,	0x54, 0x55
 mounknown:	.byte	0x02
 
@@ -842,6 +856,7 @@
 dscvideo7:	.word	0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
 dscoakvga:	.word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
 dscf1280:	.word	0x5032, 0x501c, 0x842b, 0x8419
+dsc9GXE:	.word	0x5032, 0x501c, 0x842b, 0x8419
 dsunknown:	.word	0x5032, 0x501c
 modesave:	.word	SVGA_MODE
 
diff --git a/config.in b/config.in
index 0eb6046..e2c431a 100644
--- a/config.in
+++ b/config.in
@@ -5,11 +5,11 @@
 *
 * General setup
 *
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
 bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
 bool 'TCP/IP networking' CONFIG_INET y
-bool 'Limit memory to low 16MB' CONFIG_MAX_16M y
+bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
 *
@@ -49,30 +49,39 @@
 *
 * Network device support
 *
-bool 'Network device support?' CONFIG_ETHERCARDS n
+bool 'Network device support?' CONFIG_ETHERCARDS y
 if [ "$CONFIG_ETHERCARDS" = "n" ]
 :
 : Skipping ethercard configuration options...
 :
 else
 bool 'SLIP (serial line) support' CONFIG_SLIP n
+if [ "$CONFIG_SLIP" = "y" ]
+  bool ' CSLIP compressed headers' SL_COMPRESSED y
+  bool ' SLIP debugging on' SL_DUMP y
+fi
+#bool 'PPP (point-to-point) support' CONFIG_PPP n
 bool 'PLIP (parallel port) support' CONFIG_PLIP n
 bool 'NE2000/NE1000 support' CONFIG_NE2000 n
 bool 'WD80*3 support' CONFIG_WD80x3 y
 bool 'SMC Ultra support' CONFIG_ULTRA n
 bool '3c501 support' CONFIG_EL1 n
 bool '3c503 support' CONFIG_EL2 n
+#bool '3c505 support' CONFIG_ELPLUS n
 #bool '3c507 support' CONFIG_EL16 n
-bool '3c509 support' CONFIG_EL3 n
+bool '3c509/3c579 support' CONFIG_EL3 n
 bool 'HP PCLAN support' CONFIG_HPLAN n
-bool 'AT1500 and NE2100 support' CONFIG_AT1500 n
-bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
+bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
+bool 'AT1700 support' CONFIG_AT1700 n
 #bool 'Zenith Z-Note support' CONFIG_ZNET n
 #bool 'EtherExpress support' CONFIG_EEXPRESS n
 #bool 'DEPCA support' CONFIG_DEPCA n
 #bool 'NI52** support' CONFIG_NI52 n
 #bool 'NI65** support' CONFIG_NI65 n
-#bool 'AT-LAN-TEC pocket adaptor support' CONFIG_ATP n
+#bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
+#bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 n
+bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
+#bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
 fi
 *
 bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
@@ -88,16 +97,17 @@
 bool '/proc filesystem support' CONFIG_PROC_FS y
 bool 'NFS filesystem support' CONFIG_NFS_FS y
 bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
+bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
 *
 *  character devices
 *
 bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y
-bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML n
+bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y
 bool 'Parallel printer support' CONFIG_PRINTER y
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
-bool 'QuickPort mouse support' CONFIG_QUICKPORT_MOUSE n
-if [ "$CONFIG_QUICKPORT_MOUSE" = "n" ]
 bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
+if [ "$CONFIG_PSMOUSE" = "y" ]
+bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
 fi
 bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
 bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
@@ -106,12 +116,17 @@
 *
 * Sound
 *
-bool 'Sound card support (distributed separately)' CONFIG_SOUND n
+bool 'Sound card support' CONFIG_SOUND n
 *
 * Kernel hacking
 *
-bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
+#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
 bool 'Kernel profiling support' CONFIG_PROFILE n
 if [ "$CONFIG_SCSI" = "y" ]
 bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
 fi
+if [ "$CONFIG_SOUND" = "y" ]
+  exec touch .makesound
+else
+  exec rm -f .makesound
+fi
diff --git a/drivers/FPU-emu/Makefile b/drivers/FPU-emu/Makefile
index 13f619c..78ebb64 100644
--- a/drivers/FPU-emu/Makefile
+++ b/drivers/FPU-emu/Makefile
@@ -4,13 +4,14 @@
 
 #DEBUG	= -DDEBUGGING
 DEBUG	=
+REENTRANT	= -DREENTRANT_FPU
 CFLAGS	:= $(CFLAGS) -DPARANOID $(DEBUG) -fno-builtin
 
 .c.o:
 	$(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
 
 .S.o:
-	$(CC) -D__ASSEMBLER__ -c $<
+	$(CC) -D__ASSEMBLER__ $(REENTRANT) -c $<
 
 .s.o:
 	$(CC) -c $<
@@ -31,9 +32,6 @@
 	$(AR) rcs math.a $(OBJS)
 	sync
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 	$(CPP) -D__ASSEMBLER__ -M *.S >> .depend
diff --git a/drivers/FPU-emu/README b/drivers/FPU-emu/README
index a000e4f..0b1a6f3 100644
--- a/drivers/FPU-emu/README
+++ b/drivers/FPU-emu/README
@@ -3,7 +3,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |    This program is free software; you can redistribute it and/or modify   |
  |    it under the terms of the GNU General Public License version 2 as      |
@@ -41,11 +41,13 @@
 See "Limitations" later in this file for a list of some differences.
 
 Please report bugs, etc to me at:
-       apm233m@vaxc.cc.monash.edu.au
+       billm@vaxc.cc.monash.edu.au
+  or at:
+       billm@jacobi.maths.monash.edu.au
 
 
 --Bill Metzenthen
-  July 1993
+  Sept 1993
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
diff --git a/drivers/FPU-emu/control_w.h b/drivers/FPU-emu/control_w.h
index 42bb1bf..6830321 100644
--- a/drivers/FPU-emu/control_w.h
+++ b/drivers/FPU-emu/control_w.h
@@ -3,7 +3,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
diff --git a/drivers/FPU-emu/div_small.S b/drivers/FPU-emu/div_small.S
index 0e3b71e..0225a96 100644
--- a/drivers/FPU-emu/div_small.S
+++ b/drivers/FPU-emu/div_small.S
@@ -5,7 +5,7 @@
  | Divide a 64 bit integer by a 32 bit integer & return remainder.           |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
diff --git a/drivers/FPU-emu/errors.c b/drivers/FPU-emu/errors.c
index 444a8af..cd3c03a 100644
--- a/drivers/FPU-emu/errors.c
+++ b/drivers/FPU-emu/errors.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -42,8 +42,8 @@
   byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP);
   FPU_modrm = get_fs_byte(1 + (unsigned char *) FPU_ORIG_EIP);
 
-  printk("Unimplemented FPU Opcode at eip=%08x : %02x ",
-	 FPU_ORIG_EIP, byte1);
+  printk("Unimplemented FPU Opcode at eip=%p : %02x ",
+	 (void *) FPU_ORIG_EIP, byte1);
 
   if (FPU_modrm >= 0300)
     printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
@@ -86,14 +86,14 @@
 if ( partial_status & SW_Invalid )     printk("SW: invalid operation\n");
 #endif DEBUGGING
 
-  printk("At %08x: %02x ", FPU_ORIG_EIP, byte1);
+  printk("At %p: %02x ", (void *) FPU_ORIG_EIP, byte1);
   if (FPU_modrm >= 0300)
     printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
   else
     printk("/%d, mod=%d rm=%d\n",
 	   (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
 
-  printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
+  printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
 	 partial_status & 0x8000 ? 1 : 0,   /* busy */
 	 (partial_status & 0x3800) >> 11,   /* stack top pointer */
 	 partial_status & 0x80 ? 1 : 0,     /* Error summary status */
@@ -104,7 +104,7 @@
 	 partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
 	 partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
   
-printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d     ef=%d%d%d%d%d%d\n",
+printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d     ef=%d%d%d%d%d%d\n",
 	 control_word & 0x1000 ? 1 : 0,
 	 (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
 	 (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
@@ -131,7 +131,7 @@
 	case TW_NaN:
 /*	case TW_Denormal: */
 	case TW_Infinity:
-	  printk("st(%d)  %c .%04x %04x %04x %04x e%+-6d ", i,
+	  printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
 		 r->sign ? '-' : '+',
 		 (long)(r->sigh >> 16),
 		 (long)(r->sigh & 0xFFFF),
@@ -146,7 +146,7 @@
       printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
     }
 
-  printk("[data] %c .%04x %04x %04x %04x e%+-6d ",
+  printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
 	 FPU_loaded_data.sign ? '-' : '+',
 	 (long)(FPU_loaded_data.sigh >> 16),
 	 (long)(FPU_loaded_data.sigh & 0xFFFF),
@@ -312,9 +312,9 @@
 
 /* Real operation attempted on two operands, one a NaN. */
 /* Returns nz if the exception is unmasked */
-asmlinkage int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
+asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
 {
-  FPU_REG *x;
+  FPU_REG const *x;
   int signalling;
 
   /* The default result for the case of two "equal" NaNs (signs may
diff --git a/drivers/FPU-emu/exception.h b/drivers/FPU-emu/exception.h
index 7f90ede..2e629a3 100644
--- a/drivers/FPU-emu/exception.h
+++ b/drivers/FPU-emu/exception.h
@@ -2,7 +2,7 @@
  |  exception.h                                                              |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
diff --git a/drivers/FPU-emu/fpu_arith.c b/drivers/FPU-emu/fpu_arith.c
index 471d2c6..3871a56 100644
--- a/drivers/FPU-emu/fpu_arith.c
+++ b/drivers/FPU-emu/fpu_arith.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
diff --git a/drivers/FPU-emu/fpu_asm.h b/drivers/FPU-emu/fpu_asm.h
index b20a123..8eb6014 100644
--- a/drivers/FPU-emu/fpu_asm.h
+++ b/drivers/FPU-emu/fpu_asm.h
@@ -2,7 +2,7 @@
  |  fpu_asm.h                                                                |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
diff --git a/drivers/FPU-emu/fpu_aux.c b/drivers/FPU-emu/fpu_aux.c
index 90301a4..c18194a 100644
--- a/drivers/FPU-emu/fpu_aux.c
+++ b/drivers/FPU-emu/fpu_aux.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -47,7 +47,7 @@
   FPU_entry_eip = ip_offset = 0;
 }
 
-static FUNC finit_table[] = {
+static FUNC const finit_table[] = {
   Un_impl, Un_impl, fclex, finit, Un_impl, Un_impl, Un_impl, Un_impl
 };
 
@@ -63,7 +63,7 @@
   NO_NET_INSTR_EFFECT;
 }
 
-static FUNC fstsw_table[] = {
+static FUNC const fstsw_table[] = {
   fstsw_ax, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
 };
 
@@ -78,7 +78,7 @@
 {
 }
 
-FUNC fp_nop_table[] = {
+static FUNC const fp_nop_table[] = {
   fnop, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
 };
 
diff --git a/drivers/FPU-emu/fpu_emu.h b/drivers/FPU-emu/fpu_emu.h
index 7b871fd..6d7c2db 100644
--- a/drivers/FPU-emu/fpu_emu.h
+++ b/drivers/FPU-emu/fpu_emu.h
@@ -3,7 +3,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
@@ -117,25 +117,25 @@
 /*----- Prototypes for functions written in assembler -----*/
 /* extern void reg_move(FPU_REG *a, FPU_REG *b); */
 
-asmlinkage void mul64(unsigned long long *a, unsigned long long *b,
+asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
 		      unsigned long long *result);
 asmlinkage void poly_div2(unsigned long long *x);
 asmlinkage void poly_div4(unsigned long long *x);
 asmlinkage void poly_div16(unsigned long long *x);
-asmlinkage void polynomial(unsigned accum[], unsigned x[],
-		       unsigned short terms[][4], int n);
+asmlinkage void polynomial(unsigned accum[], unsigned const x[],
+		       unsigned short const terms[][4], int const n);
 asmlinkage void normalize(FPU_REG *x);
 asmlinkage void normalize_nuo(FPU_REG *x);
-asmlinkage int reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
-		    unsigned int control_w);
-asmlinkage int reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
-		      unsigned int control_w);
-asmlinkage int reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
-		      unsigned int control_w);
-asmlinkage int reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
-		      unsigned int control_w);
-asmlinkage int reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
-		      unsigned int control_w);
+asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
+		       FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
+			 FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
+			 FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
+			 FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
+			 FPU_REG *answ, unsigned int control_w);
 asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
 asmlinkage unsigned	shrx(void *l, unsigned x);
 asmlinkage unsigned	shrxs(void *v, unsigned x);
diff --git a/drivers/FPU-emu/fpu_entry.c b/drivers/FPU-emu/fpu_entry.c
index 3ae0ff4..d511a1f 100644
--- a/drivers/FPU-emu/fpu_entry.c
+++ b/drivers/FPU-emu/fpu_entry.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | See the files "README" and "COPYING" for further copyright and warranty   |
  | information.                                                              |
@@ -63,7 +63,7 @@
 #define _df_d0_ fstp_i    /* unofficial code (17) */
 #define _df_d8_ fstp_i    /* unofficial code (1f) */
 
-static FUNC st_instr_table[64] = {
+static FUNC const st_instr_table[64] = {
   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  _df_c0_,
   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  _dd_c8_, fmulp_,  _df_c8_,
   fcom_st,  fp_nop,  __BAD__, __BAD__, _dc_d0_, fst_i_,  _de_d0_, _df_d0_,
@@ -76,7 +76,7 @@
 
 #else     /* Support only documented FPU op-codes */
 
-static FUNC st_instr_table[64] = {
+static FUNC const st_instr_table[64] = {
   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  __BAD__,
   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  __BAD__, fmulp_,  __BAD__,
   fcom_st,  fp_nop,  __BAD__, __BAD__, __BAD__, fst_i_,  __BAD__, __BAD__,
@@ -105,7 +105,7 @@
 
 /* Un-documented FPU op-codes supported by default. (see above) */
 
-static unsigned char type_table[64] = {
+static unsigned char const type_table[64] = {
   _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
   _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
   _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
@@ -118,7 +118,7 @@
 
 #else     /* Support only documented FPU op-codes */
 
-static unsigned char type_table[64] = {
+static unsigned char const type_table[64] = {
   _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
   _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
   _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
@@ -189,12 +189,19 @@
     }
 
   /* user code space? */
-  if (FPU_CS != USER_CS)
+  if (FPU_CS == KERNEL_CS)
     {
-      printk("math_emulate: %04x:%08x\n",FPU_CS,FPU_EIP);
+      printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
       panic("Math emulation needed in kernel");
     }
 
+  /* We cannot handle multiple segments yet */
+  if (FPU_CS != USER_CS || FPU_DS != USER_DS)
+    {
+      FPU_ORIG_EIP = FPU_EIP;
+      math_abort(FPU_info,SIGILL);
+    }
+
   FPU_lookahead = 1;
   if (current->flags & PF_PTRACED)
   	FPU_lookahead = 0;
@@ -523,6 +530,14 @@
 
 FPU_instruction_done:
 
+#ifdef DEBUG
+  { /* !!!!!!!!!!! */
+    static unsigned int count = 0;
+    if ( (++count % 10000) == 0 )
+	printk("%d FP instr., current=0x%04x\n", count, code);
+  } /* !!!!!!!!!!! */
+#endif DEBUG
+
   ip_offset = FPU_entry_eip;
   cs_selector = FPU_entry_op_cs;
   data_operand_offset = (unsigned long)FPU_data_address;
diff --git a/drivers/FPU-emu/fpu_etc.c b/drivers/FPU-emu/fpu_etc.c
index 7c4535f..d667759 100644
--- a/drivers/FPU-emu/fpu_etc.c
+++ b/drivers/FPU-emu/fpu_etc.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -117,7 +117,7 @@
   setcc(c);
 }
 
-static FUNC fp_etc_table[] = {
+static FUNC const fp_etc_table[] = {
   fchs, fabs, Un_impl, Un_impl, ftst_, fxam, Un_impl, Un_impl
 };
 
diff --git a/drivers/FPU-emu/fpu_proto.h b/drivers/FPU-emu/fpu_proto.h
index b7e7032..c10a4b2 100644
--- a/drivers/FPU-emu/fpu_proto.h
+++ b/drivers/FPU-emu/fpu_proto.h
@@ -7,7 +7,7 @@
 extern void stack_underflow_pop(int i);
 extern int set_precision_flag(int flags);
 asmlinkage void exception(int n);
-asmlinkage int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
+asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
 asmlinkage int arith_invalid(FPU_REG *dest);
 asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
 asmlinkage void set_precision_flag_up(void);
@@ -57,7 +57,7 @@
 extern void fp_etc(void);
 
 /* fpu_trig.c */
-extern void convert_l2reg(long *arg, FPU_REG *dest);
+extern void convert_l2reg(long const *arg, FPU_REG *dest);
 extern void trig_a(void);
 extern void trig_b(void);
 
@@ -68,28 +68,30 @@
 extern void load_store_instr(char type);
 
 /* poly_2xm1.c */
-extern int poly_2xm1(FPU_REG *arg, FPU_REG *result);
+extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
 
 /* poly_atan.c */
 extern void poly_atan(FPU_REG *arg);
 extern void poly_add_1(FPU_REG *src);
 
 /* poly_l2.c */
-extern void poly_l2(FPU_REG *arg, FPU_REG *result);
-extern int poly_l2p1(FPU_REG *arg, FPU_REG *result);
+extern void poly_l2(FPU_REG const *arg, FPU_REG *result);
+extern int poly_l2p1(FPU_REG const *arg, FPU_REG *result);
 
 /* poly_sin.c */
-extern void poly_sine(FPU_REG *arg, FPU_REG *result);
+extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
 
 /* poly_tan.c */
-extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert);
+extern void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert);
 
 /* reg_add_sub.c */
-extern int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
-extern int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
+extern int reg_add(FPU_REG const *a, FPU_REG const *b,
+		   FPU_REG *dest, int control_w);
+extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
+		   FPU_REG *dest, int control_w);
 
 /* reg_compare.c */
-extern int compare(FPU_REG *b);
+extern int compare(FPU_REG const *b);
 extern int compare_st_data(void);
 extern void fcom_st(void);
 extern void fcompst(void);
@@ -124,4 +126,5 @@
 extern void fsave(void);
 
 /* reg_mul.c */
-extern int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w);
+extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
+		   FPU_REG *dest, unsigned int control_w);
diff --git a/drivers/FPU-emu/fpu_system.h b/drivers/FPU-emu/fpu_system.h
index be6b0bb..e8e50c5 100644
--- a/drivers/FPU-emu/fpu_system.h
+++ b/drivers/FPU-emu/fpu_system.h
@@ -2,7 +2,7 @@
  |  fpu_system.h                                                             |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
diff --git a/drivers/FPU-emu/fpu_trig.c b/drivers/FPU-emu/fpu_trig.c
index d20c4db..76db016 100644
--- a/drivers/FPU-emu/fpu_trig.c
+++ b/drivers/FPU-emu/fpu_trig.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -122,7 +122,7 @@
 
 
 /* Convert a long to register */
-void convert_l2reg(long *arg, FPU_REG *dest)
+void convert_l2reg(long const *arg, FPU_REG *dest)
 {
   long num = *arg;
 
@@ -1696,7 +1696,7 @@
 
 /*---------------------------------------------------------------------------*/
 
-static FUNC trig_table_a[] = {
+static FUNC const trig_table_a[] = {
   f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
 };
 
@@ -1706,7 +1706,7 @@
 }
 
 
-static FUNC trig_table_b[] =
+static FUNC const trig_table_b[] =
   {
     fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
   };
diff --git a/drivers/FPU-emu/get_address.c b/drivers/FPU-emu/get_address.c
index 86ae753..98f97d6 100644
--- a/drivers/FPU-emu/get_address.c
+++ b/drivers/FPU-emu/get_address.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
diff --git a/drivers/FPU-emu/load_store.c b/drivers/FPU-emu/load_store.c
index 6e4b3d9..2966b07 100644
--- a/drivers/FPU-emu/load_store.c
+++ b/drivers/FPU-emu/load_store.c
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -35,7 +35,7 @@
 #define pop_0()	{ pop_ptr->tag = TW_Empty; top++; }
 
 
-static unsigned char type_table[32] = {
+static unsigned char const type_table[32] = {
   _PUSH_, _PUSH_, _PUSH_, _PUSH_,
   _null_, _null_, _null_, _null_,
   _REG0_, _REG0_, _REG0_, _REG0_,
diff --git a/drivers/FPU-emu/poly_2xm1.c b/drivers/FPU-emu/poly_2xm1.c
index 7e8aec7..4844aa7 100644
--- a/drivers/FPU-emu/poly_2xm1.c
+++ b/drivers/FPU-emu/poly_2xm1.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -17,7 +17,7 @@
 
 
 #define	HIPOWER	13
-static unsigned short	lterms[HIPOWER][4] =
+static unsigned short const	lterms[HIPOWER][4] =
 	{
 	{ 0x79b5, 0xd1cf, 0x17f7, 0xb172 },
 	{ 0x1b56, 0x058b, 0x7bff, 0x3d7f },
@@ -38,7 +38,7 @@
 /*--- poly_2xm1() -----------------------------------------------------------+
  | Requires a positive argument which is TW_Valid and < 1.                   |
  +---------------------------------------------------------------------------*/
-int	poly_2xm1(FPU_REG *arg, FPU_REG *result)
+int	poly_2xm1(FPU_REG const *arg, FPU_REG *result)
 {
   short		exponent;
   long long     Xll;
diff --git a/drivers/FPU-emu/poly_atan.c b/drivers/FPU-emu/poly_atan.c
index e0a0645..ea606f9 100644
--- a/drivers/FPU-emu/poly_atan.c
+++ b/drivers/FPU-emu/poly_atan.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -17,7 +17,7 @@
 
 
 #define	HIPOWERon	6	/* odd poly, negative terms */
-static unsigned oddnegterms[HIPOWERon][2] =
+static unsigned const oddnegterms[HIPOWERon][2] =
 {
   { 0x00000000, 0x00000000 }, /* for + 1.0 */
   { 0x763b6f3d, 0x1adc4428 },
@@ -28,7 +28,7 @@
 } ;
 
 #define	HIPOWERop	6	/* odd poly, positive terms */
-static unsigned	oddplterms[HIPOWERop][2] =
+static unsigned const	oddplterms[HIPOWERop][2] =
 {
   { 0xa6f67cb8, 0x94d910bd },
   { 0xa02ffab4, 0x0a43cb45 },
@@ -38,7 +38,7 @@
   { 0xf1dd2dbf, 0x000a530a }
 };
 
-static unsigned long long denomterm = 0xea2e6612fc4bd208LL;
+static unsigned long long const denomterm = 0xea2e6612fc4bd208LL;
 
 
 /*--- poly_atan() -----------------------------------------------------------+
diff --git a/drivers/FPU-emu/poly_div.S b/drivers/FPU-emu/poly_div.S
index cab7d72..a672933 100644
--- a/drivers/FPU-emu/poly_div.S
+++ b/drivers/FPU-emu/poly_div.S
@@ -5,7 +5,7 @@
  | A set of functions to divide 64 bit integers by fixed numbers.            |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void poly_div2(unsigned long long *x)                                   |
diff --git a/drivers/FPU-emu/poly_l2.c b/drivers/FPU-emu/poly_l2.c
index 49085a0..6d36fef 100644
--- a/drivers/FPU-emu/poly_l2.c
+++ b/drivers/FPU-emu/poly_l2.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -19,7 +19,7 @@
 
 
 #define	HIPOWER	9
-static unsigned short	lterms[HIPOWER][4] =
+static unsigned short const	lterms[HIPOWER][4] =
 	{
 	/* Ideal computation with these coeffs gives about
 	   64.6 bit rel accuracy. */
@@ -40,7 +40,7 @@
 /*--- poly_l2() -------------------------------------------------------------+
  |   Base 2 logarithm by a polynomial approximation.                         |
  +---------------------------------------------------------------------------*/
-void	poly_l2(FPU_REG *arg, FPU_REG *result)
+void	poly_l2(FPU_REG const *arg, FPU_REG *result)
 {
   short		  exponent;
   char		  zero;		/* flag for an Xx == 0 */
@@ -224,7 +224,7 @@
  |   Base 2 logarithm by a polynomial approximation.                         |
  |   log2(x+1)                                                               |
  +---------------------------------------------------------------------------*/
-int	poly_l2p1(FPU_REG *arg, FPU_REG *result)
+int	poly_l2p1(FPU_REG const *arg, FPU_REG *result)
 {
   char		sign = 0;
   unsigned long long     Xsq;
diff --git a/drivers/FPU-emu/poly_mul64.S b/drivers/FPU-emu/poly_mul64.S
index 52fcee7..d62e24d 100644
--- a/drivers/FPU-emu/poly_mul64.S
+++ b/drivers/FPU-emu/poly_mul64.S
@@ -4,7 +4,7 @@
  | Multiply two 64 bit integers.                                             |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void mul64(long long *a, long long *b, long long *result)               |
diff --git a/drivers/FPU-emu/poly_sin.c b/drivers/FPU-emu/poly_sin.c
index 5076741..b6a0507 100644
--- a/drivers/FPU-emu/poly_sin.c
+++ b/drivers/FPU-emu/poly_sin.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -18,7 +18,7 @@
 
 
 #define	HIPOWER	5
-static unsigned short	lterms[HIPOWER][4] =
+static unsigned short const	lterms[HIPOWER][4] =
 	{
 	{ 0x846a, 0x42d1, 0xb544, 0x921f},
 	{ 0xe110, 0x75aa, 0xbc67, 0x1466},
@@ -27,7 +27,7 @@
 	{ 0xda03, 0x06aa, 0x0000, 0x0000},
 	};
 
-static unsigned short	negterms[HIPOWER][4] =
+static unsigned short const	negterms[HIPOWER][4] =
 	{
 	{ 0x95ed, 0x2df2, 0xe731, 0xa55d},
 	{ 0xd159, 0xe62b, 0xd2cc, 0x0132},
@@ -40,7 +40,7 @@
 /*--- poly_sine() -----------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void	poly_sine(FPU_REG *arg, FPU_REG *result)
+void	poly_sine(FPU_REG const *arg, FPU_REG *result)
 {
   short	exponent;
   FPU_REG	fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
diff --git a/drivers/FPU-emu/poly_tan.c b/drivers/FPU-emu/poly_tan.c
index 4c8a41b..6b11988 100644
--- a/drivers/FPU-emu/poly_tan.c
+++ b/drivers/FPU-emu/poly_tan.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -17,7 +17,7 @@
 
 
 #define	HIPOWERop	3	/* odd poly, positive terms */
-static unsigned short	oddplterms[HIPOWERop][4] =
+static unsigned short const	oddplterms[HIPOWERop][4] =
 	{
 	{ 0x846a, 0x42d1, 0xb544, 0x921f},
 	{ 0x6fb2, 0x0215, 0x95c0, 0x099c},
@@ -25,21 +25,21 @@
 	};
 
 #define	HIPOWERon	2	/* odd poly, negative terms */
-static unsigned short	oddnegterms[HIPOWERon][4] =
+static unsigned short const	oddnegterms[HIPOWERon][4] =
 	{
 	{ 0x6906, 0xe205, 0x25c8, 0x8838},
 	{ 0x1dd7, 0x3fe3, 0x944e, 0x002c}
 	};
 
 #define	HIPOWERep	2	/* even poly, positive terms */
-static unsigned short	evenplterms[HIPOWERep][4] =
+static unsigned short const	evenplterms[HIPOWERep][4] =
 	{
 	{ 0xdb8f, 0x3761, 0x1432, 0x2acf},
 	{ 0x16eb, 0x13c1, 0x3099, 0x0003}
 	};
 
 #define	HIPOWERen	2	/* even poly, negative terms */
-static unsigned short	evennegterms[HIPOWERen][4] =
+static unsigned short const	evennegterms[HIPOWERen][4] =
 	{
 	{ 0x3a7c, 0xe4c5, 0x7f87, 0x2945},
 	{ 0x572b, 0x664c, 0xc543, 0x018c}
@@ -49,7 +49,7 @@
 /*--- poly_tan() ------------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void	poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert)
+void	poly_tan(FPU_REG const *arg, FPU_REG *result, int invert)
 {
   short		exponent;
   FPU_REG       odd_poly, even_poly, pos_poly, neg_poly;
@@ -61,7 +61,7 @@
 
 #ifdef PARANOID
   if ( arg->sign != 0 )	/* Can't hack a number < 0.0 */
-    { arith_invalid(y_reg); return; }  /* Need a positive number */
+    { arith_invalid(result); return; }  /* Need a positive number */
 #endif PARANOID
 
   arg_signif = significand(arg);
@@ -142,8 +142,8 @@
 
   /* Now ready to copy the results */
   if ( invert )
-    { reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION); }
+    { reg_div(&even_poly, &odd_poly, result, FULL_PRECISION); }
   else
-    { reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION); }
+    { reg_div(&odd_poly, &even_poly, result, FULL_PRECISION); }
 
 }
diff --git a/drivers/FPU-emu/polynomial.S b/drivers/FPU-emu/polynomial.S
index d3407ab..fa4da12 100644
--- a/drivers/FPU-emu/polynomial.S
+++ b/drivers/FPU-emu/polynomial.S
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2],    |
@@ -22,7 +22,7 @@
 #include "fpu_asm.h"
 
 
-/*	#define	EXTRA_PRECISE	Do not use: not complete */
+/*	#define	EXTRA_PRECISE	// Do not use: not complete */
 
 #define	TERM_SIZE	$8
 #define	SUM_MS		-20(%ebp)	/* sum ms long */
@@ -70,7 +70,7 @@
 
 	movl	SUM_MIDDLE,%eax
 	mull	(%esi)			/* x ls long */
-/*	movl	%eax,-16(%ebp)		Not needed */
+/*	movl	%eax,-16(%ebp)		// Not needed */
 	movl	%edx,ACCUM_LS
 
 	movl	SUM_MIDDLE,%eax
@@ -119,7 +119,7 @@
 
 L_accum_done:
 #ifdef EXTRA_PRECISE
-/* And round the result */
+/* Round the result */
 	testb	$128,SUM_LS_HI
 	je	L_poly_done
 
diff --git a/drivers/FPU-emu/reg_add_sub.c b/drivers/FPU-emu/reg_add_sub.c
index 890484e..d70889b 100644
--- a/drivers/FPU-emu/reg_add_sub.c
+++ b/drivers/FPU-emu/reg_add_sub.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -22,7 +22,7 @@
 #include "fpu_system.h"
 
 
-int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
 {
   char saved_sign = dest->sign;
   int diff;
@@ -162,7 +162,7 @@
 
 
 /* Subtract b from a.  (a-b) -> dest */
-int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
 {
   char saved_sign = dest->sign;
   int diff;
diff --git a/drivers/FPU-emu/reg_compare.c b/drivers/FPU-emu/reg_compare.c
index d1ec646..619ee0b 100644
--- a/drivers/FPU-emu/reg_compare.c
+++ b/drivers/FPU-emu/reg_compare.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -21,7 +21,7 @@
 #include "status_w.h"
 
 
-int compare(FPU_REG *b)
+int compare(FPU_REG const *b)
 {
   int diff;
 
diff --git a/drivers/FPU-emu/reg_constant.c b/drivers/FPU-emu/reg_constant.c
index 1f63051..b2aa574 100644
--- a/drivers/FPU-emu/reg_constant.c
+++ b/drivers/FPU-emu/reg_constant.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -16,47 +16,47 @@
 #include "reg_constant.h"
 
 
-FPU_REG CONST_1    = { SIGN_POS, TW_Valid, EXP_BIAS,
+FPU_REG const CONST_1    = { SIGN_POS, TW_Valid, EXP_BIAS,
 			    0x00000000, 0x80000000 };
-FPU_REG CONST_2    = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+FPU_REG const CONST_2    = { SIGN_POS, TW_Valid, EXP_BIAS+1,
 			    0x00000000, 0x80000000 };
-FPU_REG CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
 			    0x00000000, 0x80000000 };
-FPU_REG CONST_L2T  = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+FPU_REG const CONST_L2T  = { SIGN_POS, TW_Valid, EXP_BIAS+1,
 			    0xcd1b8afe, 0xd49a784b };
-FPU_REG CONST_L2E  = { SIGN_POS, TW_Valid, EXP_BIAS,
+FPU_REG const CONST_L2E  = { SIGN_POS, TW_Valid, EXP_BIAS,
 			    0x5c17f0bc, 0xb8aa3b29 };
-FPU_REG CONST_PI   = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+FPU_REG const CONST_PI   = { SIGN_POS, TW_Valid, EXP_BIAS+1,
 			    0x2168c235, 0xc90fdaa2 };
-FPU_REG CONST_PI2  = { SIGN_POS, TW_Valid, EXP_BIAS,
+FPU_REG const CONST_PI2  = { SIGN_POS, TW_Valid, EXP_BIAS,
 			    0x2168c235, 0xc90fdaa2 };
-FPU_REG CONST_PI4  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+FPU_REG const CONST_PI4  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
 			    0x2168c235, 0xc90fdaa2 };
-FPU_REG CONST_LG2  = { SIGN_POS, TW_Valid, EXP_BIAS-2,
+FPU_REG const CONST_LG2  = { SIGN_POS, TW_Valid, EXP_BIAS-2,
 			    0xfbcff799, 0x9a209a84 };
-FPU_REG CONST_LN2  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+FPU_REG const CONST_LN2  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
 			    0xd1cf79ac, 0xb17217f7 };
 
 /* Extra bits to take pi/2 to more than 128 bits precision. */
-FPU_REG CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
+FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
 			    0xfc8f8cbb, 0xece675d1 };
 
 /* Only the sign (and tag) is used in internal zeroes */
-FPU_REG CONST_Z    = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
+FPU_REG const CONST_Z    = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
 
 /* Only the sign and significand (and tag) are used in internal NaNs */
 /* The 80486 never generates one of these 
-FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
  */
 /* This is the real indefinite QNaN */
-FPU_REG CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
+FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
 
 /* Only the sign (and tag) is used in internal infinities */
-FPU_REG CONST_INF  = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
+FPU_REG const CONST_INF  = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
 
 
 
-static void fld_const(FPU_REG *c)
+static void fld_const(FPU_REG const *c)
 {
   FPU_REG *st_new_ptr;
 
diff --git a/drivers/FPU-emu/reg_constant.h b/drivers/FPU-emu/reg_constant.h
index 23ba06d..b7db97e 100644
--- a/drivers/FPU-emu/reg_constant.h
+++ b/drivers/FPU-emu/reg_constant.h
@@ -2,7 +2,7 @@
  |  reg_constant.h                                                           |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
@@ -11,21 +11,21 @@
 
 #include "fpu_emu.h"
 
-extern FPU_REG CONST_1;
-extern FPU_REG CONST_2;
-extern FPU_REG CONST_HALF;
-extern FPU_REG CONST_L2T;
-extern FPU_REG CONST_L2E;
-extern FPU_REG CONST_PI;
-extern FPU_REG CONST_PI2;
-extern FPU_REG CONST_PI2extra;
-extern FPU_REG CONST_PI4;
-extern FPU_REG CONST_LG2;
-extern FPU_REG CONST_LN2;
-extern FPU_REG CONST_Z;
-extern FPU_REG CONST_PINF;
-extern FPU_REG CONST_INF;
-extern FPU_REG CONST_MINF;
-extern FPU_REG CONST_QNaN;
+extern FPU_REG const CONST_1;
+extern FPU_REG const CONST_2;
+extern FPU_REG const CONST_HALF;
+extern FPU_REG const CONST_L2T;
+extern FPU_REG const CONST_L2E;
+extern FPU_REG const CONST_PI;
+extern FPU_REG const CONST_PI2;
+extern FPU_REG const CONST_PI2extra;
+extern FPU_REG const CONST_PI4;
+extern FPU_REG const CONST_LG2;
+extern FPU_REG const CONST_LN2;
+extern FPU_REG const CONST_Z;
+extern FPU_REG const CONST_PINF;
+extern FPU_REG const CONST_INF;
+extern FPU_REG const CONST_MINF;
+extern FPU_REG const CONST_QNaN;
 
 #endif _REG_CONSTANT_H_
diff --git a/drivers/FPU-emu/reg_div.S b/drivers/FPU-emu/reg_div.S
index 24acaec..558c594 100644
--- a/drivers/FPU-emu/reg_div.S
+++ b/drivers/FPU-emu/reg_div.S
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,                     |
@@ -17,6 +17,7 @@
 #include "exception.h"
 #include "fpu_asm.h"
 
+
 .text
 	.align 2
 
@@ -24,6 +25,9 @@
 _reg_div:
 	pushl	%ebp
 	movl	%esp,%ebp
+#ifdef REENTRANT_FPU
+	subl	$28,%esp	/* Needed by divide_kernel */
+#endif REENTRANT_FPU
 
 	pushl	%esi
 	pushl	%edi
@@ -45,7 +49,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xL_arg1_not_denormal:
 	cmpl	EXP_UNDER,EXP(%ebx)
@@ -53,7 +57,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xL_arg2_not_denormal:
 #endif DENORM_OPERAND
@@ -86,7 +90,6 @@
 L_arg1_NaN:
 L_arg2_NaN:
 	pushl	%edi			/* Destination */
-/*	pushl	%ebx */
 	pushl	%esi
 	pushl	%ebx			/* Ordering is important here */
 	call	_real_2op_NaN
@@ -125,7 +128,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 #endif DENORM_OPERAND
 
 	jmp	L_copy_arg1		/* Answer is Inf */
@@ -164,7 +167,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 #endif DENORM_OPERAND
 
 	jmp	L_return_zero		/* Answer is zero */
@@ -184,7 +187,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 #endif DENORM_OPERAND
 
 L_copy_arg1:
@@ -211,7 +214,11 @@
 	xorl	%eax,%eax		/* Valid result */
 
 LDiv_exit:
+#ifdef REENTRANT_FPU
+	leal	-40(%ebp),%esp
+#else
 	leal	-12(%ebp),%esp
+#endif REENTRANT_FPU
 
 	popl	%ebx
 	popl	%edi
diff --git a/drivers/FPU-emu/reg_ld_str.c b/drivers/FPU-emu/reg_ld_str.c
index d07c7bf..c9d7495 100644
--- a/drivers/FPU-emu/reg_ld_str.c
+++ b/drivers/FPU-emu/reg_ld_str.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
diff --git a/drivers/FPU-emu/reg_mul.c b/drivers/FPU-emu/reg_mul.c
index a7d48ad..cd21beb 100644
--- a/drivers/FPU-emu/reg_mul.c
+++ b/drivers/FPU-emu/reg_mul.c
@@ -5,7 +5,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -21,7 +21,8 @@
 
 
 /* This routine must be called with non-empty source registers */
-int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
+int reg_mul(FPU_REG const *a, FPU_REG const *b,
+	    FPU_REG *dest, unsigned int control_w)
 {
   char saved_sign = dest->sign;
   char sign = (a->sign ^ b->sign);
diff --git a/drivers/FPU-emu/reg_norm.S b/drivers/FPU-emu/reg_norm.S
index f63d74a..8ce60a2 100644
--- a/drivers/FPU-emu/reg_norm.S
+++ b/drivers/FPU-emu/reg_norm.S
@@ -3,7 +3,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Normalize the value in a FPU_REG.                                         |
  |                                                                           |
@@ -39,7 +39,6 @@
 	orl	%eax,%eax
 	jz	L_zero		/* The contents are zero */
 
-/* L_shift_32: */
 	movl	%eax,%edx
 	xorl	%eax,%eax
 	subl	$32,EXP(%ebx)	/* This can cause an underflow */
@@ -109,7 +108,6 @@
 	orl	%eax,%eax
 	jz	L_zero		/* The contents are zero */
 
-/* L_nuo_shift_32: */
 	movl	%eax,%edx
 	xorl	%eax,%eax
 	subl	$32,EXP(%ebx)	/* This can cause an underflow */
diff --git a/drivers/FPU-emu/reg_round.S b/drivers/FPU-emu/reg_round.S
index 77a8a3a..32f2b2e 100644
--- a/drivers/FPU-emu/reg_round.S
+++ b/drivers/FPU-emu/reg_round.S
@@ -6,11 +6,11 @@
  |                                                                           |
  | Copyright (C) 1993                                                        |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | This code has four possible entry points.                                 |
  | The following must be entered by a jmp intruction:                        |
- |   FPU_round, FPU_round_sqrt, and FPU_Arith_exit.                          |
+ |   fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit.                  |
  |                                                                           |
  | The _round_reg entry point is intended to be used by C code.              |
  | From C, call as:                                                          |
@@ -24,14 +24,14 @@
 /*---------------------------------------------------------------------------+
  | Four entry points.                                                        |
  |                                                                           |
- | Needed by both the FPU_round and FPU_round_sqrt entry points:             |
+ | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points:     |
  |  %eax:%ebx  64 bit significand                                            |
  |  %edx       32 bit extension of the significand                           |
  |  %edi       pointer to an FPU_REG for the result to be stored             |
  |  stack      calling function must have set up a C stack frame and         |
  |             pushed %esi, %edi, and %ebx                                   |
  |                                                                           |
- | Needed just for the FPU_round_sqrt entry point:                           |
+ | Needed just for the fpu_reg_round_sqrt entry point:                       |
  |  %cx  A control word in the same format as the FPU control word.          |
  | Otherwise, PARAM4 must give such a value.                                 |
  |                                                                           |
@@ -81,18 +81,30 @@
 #define	DENORMAL	$1
 #define	UNMASKED_UNDERFLOW $2
 
+
+#ifdef REENTRANT_FPU
+/*	Make the code re-entrant by putting
+	local storage on the stack: */
+#define FPU_bits_lost	(%esp)
+#define FPU_denormal	1(%esp)
+
+#else
+/*	Not re-entrant, so we can gain speed by putting
+	local storage in a static area: */
 .data
 	.align 2,0
 FPU_bits_lost:
 	.byte	0
 FPU_denormal:
 	.byte	0
+#endif REENTRANT_FPU
+
 
 .text
 	.align 2,144
-.globl FPU_round
-.globl FPU_round_sqrt
-.globl FPU_Arith_exit
+.globl fpu_reg_round
+.globl fpu_reg_round_sqrt
+.globl fpu_Arith_exit
 .globl _round_reg
 
 /* Entry point when called from C */
@@ -108,19 +120,21 @@
 	movl	SIGL(%edi),%ebx
 	movl	PARAM2,%edx
 	movl	PARAM3,%ecx
-	jmp	FPU_round_sqrt
+	jmp	fpu_reg_round_sqrt
 
-FPU_round:		/* Normal entry point */
+fpu_reg_round:			/* Normal entry point */
 	movl	PARAM4,%ecx
 
-FPU_round_sqrt:		/* Entry point from wm_sqrt.S */
+fpu_reg_round_sqrt:		/* Entry point from wm_sqrt.S */
+
+#ifdef REENTRANT_FPU
+	pushl	%ebx		/* adjust the stack pointer */
+#endif REENTRANT_FPU
 
 #ifdef PARANOID
-/*
- * Cannot use this here yet
- *	orl	%eax,%eax
- *	jns	L_entry_bugged
- */
+/* Cannot use this here yet */
+/*	orl	%eax,%eax */
+/*	jns	L_entry_bugged */
 #endif PARANOID
 
 	cmpl	EXP_UNDER,EXP(%edi)
@@ -218,7 +232,7 @@
 	andl	$0x000000ff,%ecx
 	orl	%ebx,%ecx
 	orl	%edx,%ecx
-	jz	LRe_normalise			/* No truncation needed */
+	jz	LRe_normalise		/* No truncation needed */
 
 LDo_truncate_24:
 	andl	$0xffffff00,%eax	/* Truncate to 24 bits */
@@ -392,7 +406,12 @@
 	cmpl	EXP_OVER,EXP(%edi)
 	jge	L_overflow
 
-FPU_Arith_exit:
+fpu_reg_round_exit:
+#ifdef REENTRANT_FPU
+	popl	%ebx		/* adjust the stack pointer */
+#endif REENTRANT_FPU
+
+fpu_Arith_exit:
 	popl	%ebx
 	popl	%edi
 	popl	%esi
@@ -427,8 +446,8 @@
  * have to be undone later...
  */
 xMake_denorm:
-	/* The action to be taken depends upon whether the underflow */
-	/* exception is masked */
+	/* The action to be taken depends upon whether the underflow
+	   exception is masked */
 	testb	CW_Underflow,%cl		/* Underflow mask. */
 	jz	xUnmasked_underflow		/* Do not make a denormal. */
 
@@ -601,7 +620,7 @@
 	push	%edi
 	call	_arith_overflow
 	pop	%edi
-	jmp	FPU_Arith_exit
+	jmp	fpu_reg_round_exit
 
 
 xSignal_underflow:
@@ -643,5 +662,5 @@
 	popl	%ebx
 L_exception_exit:
 	mov	$1,%eax
-	jmp	FPU_Arith_exit
+	jmp	fpu_reg_round_exit
 #endif PARANOID
diff --git a/drivers/FPU-emu/reg_u_add.S b/drivers/FPU-emu/reg_u_add.S
index 5c42e8a..4410f8f 100644
--- a/drivers/FPU-emu/reg_u_add.S
+++ b/drivers/FPU-emu/reg_u_add.S
@@ -7,7 +7,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
@@ -34,7 +34,6 @@
 _reg_u_add:
 	pushl	%ebp
 	movl	%esp,%ebp
-/*	subl	$16,%esp */
 	pushl	%esi
 	pushl	%edi
 	pushl	%ebx
@@ -48,7 +47,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp1_not_denorm:
 	cmpl	EXP_UNDER,EXP(%edi)
@@ -56,15 +55,13 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp2_not_denorm:
 #endif DENORM_OPERAND
 
-/*	xorl	%ecx,%ecx */
 	movl	EXP(%esi),%ecx
 	subl	EXP(%edi),%ecx		/* exp1 - exp2 */
-/*	jnc	L_arg1_larger */
 	jge	L_arg1_larger
 
 	/* num1 is smaller */
@@ -82,12 +79,12 @@
 
 L_accum_loaded:
 	movl	PARAM3,%edi		/* destination */
-/*	movb	SIGN(%esi),%dl */
-/*	movb	%dl,SIGN(%edi)	*/	/* Copy the sign from the first arg */
+/*	movb	SIGN(%esi),%dl
+	movb	%dl,SIGN(%edi) */	/* Copy the sign from the first arg */
 
 
 	movl	EXP(%esi),%edx
-	movl	%edx,EXP(%edi)	/* Copy exponent to destination */
+	movl	%edx,EXP(%edi)		/* Copy exponent to destination */
 
 	xorl	%edx,%edx		/* clear the extension */
 
@@ -170,7 +167,7 @@
 	incl	EXP(%edi)
 
 L_round_the_result:
-	jmp	FPU_round	/* Round the result */
+	jmp	fpu_reg_round	/* Round the result */
 
 
 
diff --git a/drivers/FPU-emu/reg_u_div.S b/drivers/FPU-emu/reg_u_div.S
index 49b1328..0098324 100644
--- a/drivers/FPU-emu/reg_u_div.S
+++ b/drivers/FPU-emu/reg_u_div.S
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -29,27 +29,43 @@
 /* #define	dSIGH(x)	4(x) */
 
 
+#ifdef REENTRANT_FPU
+/*
+	Local storage on the stack:
+	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+	Overflow flag:	ovfl_flag
+ */
+#define FPU_accum_3	-4(%ebp)
+#define FPU_accum_2	-8(%ebp)
+#define FPU_accum_1	-12(%ebp)
+#define FPU_accum_0	-16(%ebp)
+#define FPU_result_1	-20(%ebp)
+#define FPU_result_2	-24(%ebp)
+#define FPU_ovfl_flag	-28(%ebp)
+
+#else
 .data
 /*
-	Local storage:
-	Result:		accum_3:accum_2:accum_1:accum_0
+	Local storage in a static area:
+	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
 	Overflow flag:	ovfl_flag
  */
 	.align 2,0
-accum_3:
+FPU_accum_3:
 	.long	0
-accum_2:
+FPU_accum_2:
 	.long	0
-accum_1:
+FPU_accum_1:
 	.long	0
-accum_0:
+FPU_accum_0:
 	.long	0
-result_1:
+FPU_result_1:
 	.long	0
-result_2:
+FPU_result_2:
 	.long	0
-ovfl_flag:
+FPU_ovfl_flag:
 	.byte	0
+#endif REENTRANT_FPU
 
 
 .text
@@ -62,6 +78,9 @@
 _reg_u_div:
 	pushl	%ebp
 	movl	%esp,%ebp
+#ifdef REENTRANT_FPU
+	subl	$28,%esp
+#endif REENTRANT_FPU
 
 	pushl	%esi
 	pushl	%edi
@@ -78,7 +97,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp1_not_denorm:
 	movl	EXP(%ebx),%eax
@@ -87,7 +106,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp2_not_denorm:
 #endif DENORM_OPERAND
@@ -110,7 +129,7 @@
 	movl	SIGL(%esi),%eax	/* Dividend */
 
 	cmpl	%ecx,%edx
-	setaeb	ovfl_flag	/* Keep a record */
+	setaeb	FPU_ovfl_flag	/* Keep a record */
 	jb	L_no_adjust
 
 	subl	%ecx,%edx	/* Prevent the overflow */
@@ -118,18 +137,18 @@
 L_no_adjust:
 	/* Divide the 64 bit number by the 32 bit denominator */
 	divl	%ecx
-	movl	%eax,result_2
+	movl	%eax,FPU_result_2
 
 	/* Work on the remainder of the first division */
 	xorl	%eax,%eax
 	divl	%ecx
-	movl	%eax,result_1
+	movl	%eax,FPU_result_1
 
 	/* Work on the remainder of the 64 bit division */
 	xorl	%eax,%eax
 	divl	%ecx
 
-	testb	$255,ovfl_flag	/* was the num > denom ? */
+	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
 	je	L_no_overflow
 
 	/* Do the shifting here */
@@ -138,8 +157,8 @@
 
 	/* shift the mantissa right one bit */
 	stc			/* To set the ms bit */
-	rcrl	result_2
-	rcrl	result_1
+	rcrl	FPU_result_2
+	rcrl	FPU_result_1
 	rcrl	%eax
 
 L_no_overflow:
@@ -167,21 +186,21 @@
 L_Full_Division:
 	/* Save extended dividend in local register */
 	movl	SIGL(%esi),%eax
-	movl	%eax,accum_2
+	movl	%eax,FPU_accum_2
 	movl	SIGH(%esi),%eax
-	movl	%eax,accum_3
+	movl	%eax,FPU_accum_3
 	xorl	%eax,%eax
-	movl	%eax,accum_1	/* zero the extension */
-	movl	%eax,accum_0	/* zero the extension */
+	movl	%eax,FPU_accum_1	/* zero the extension */
+	movl	%eax,FPU_accum_0	/* zero the extension */
 
 	movl	SIGL(%esi),%eax	/* Get the current num */
 	movl	SIGH(%esi),%edx
 
 /*----------------------------------------------------------------------*/
-/* Initialization done */
-/* Do the first 32 bits */
+/* Initialization done.
+   Do the first 32 bits. */
 
-	movb	$0,ovfl_flag
+	movb	$0,FPU_ovfl_flag
 	cmpl	SIGH(%ebx),%edx	/* Test for imminent overflow */
 	jb	LLess_than_1
 	ja	LGreater_than_1
@@ -191,16 +210,16 @@
 
 LGreater_than_1:
 /* The dividend is greater or equal, would cause overflow */
-	setaeb	ovfl_flag		/* Keep a record */
+	setaeb	FPU_ovfl_flag		/* Keep a record */
 
 	subl	SIGL(%ebx),%eax
 	sbbl	SIGH(%ebx),%edx	/* Prevent the overflow */
-	movl	%eax,accum_2
-	movl	%edx,accum_3
+	movl	%eax,FPU_accum_2
+	movl	%edx,FPU_accum_3
 
 LLess_than_1:
 /* At this point, we have a dividend < divisor, with a record of
-   adjustment in ovfl_flag */
+   adjustment in FPU_ovfl_flag */
 
 	/* We will divide by a number which is too large */
 	movl	SIGH(%ebx),%ecx
@@ -217,19 +236,19 @@
 				   denom ms dw */
 
 LFirst_div_done:
-	movl	%eax,result_2	/* Put the result in the answer */
+	movl	%eax,FPU_result_2	/* Put the result in the answer */
 
 	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
 
-	subl	%eax,accum_2	/* Subtract from the num local reg */
-	sbbl	%edx,accum_3
+	subl	%eax,FPU_accum_2	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_3
 
-	movl	result_2,%eax	/* Get the result back */
+	movl	FPU_result_2,%eax	/* Get the result back */
 	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
 
-	subl	%eax,accum_1	/* Subtract from the num local reg */
-	sbbl	%edx,accum_2
-	sbbl	$0,accum_3
+	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_2
+	sbbl	$0,FPU_accum_3
 	je	LDo_2nd_32_bits		/* Must check for non-zero result here */
 
 #ifdef PARANOID
@@ -237,25 +256,25 @@
 #endif PARANOID
 
 	/* need to subtract another once of the denom */
-	incl	result_2	/* Correct the answer */
+	incl	FPU_result_2	/* Correct the answer */
 
 	movl	SIGL(%ebx),%eax
 	movl	SIGH(%ebx),%edx
-	subl	%eax,accum_1	/* Subtract from the num local reg */
-	sbbl	%edx,accum_2
+	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_2
 
 #ifdef PARANOID
-	sbbl	$0,accum_3
+	sbbl	$0,FPU_accum_3
 	jne	L_bugged_1	/* Must check for non-zero result here */
 #endif PARANOID
 
 /*----------------------------------------------------------------------*/
 /* Half of the main problem is done, there is just a reduced numerator
-   to handle now */
-/* Work with the second 32 bits, accum_0 not used from now on */
+   to handle now.
+   Work with the second 32 bits, FPU_accum_0 not used from now on */
 LDo_2nd_32_bits:
-	movl	accum_2,%edx	/* get the reduced num */
-	movl	accum_1,%eax
+	movl	FPU_accum_2,%edx	/* get the reduced num */
+	movl	FPU_accum_1,%eax
 
 	/* need to check for possible subsequent overflow */
 	cmpl	SIGH(%ebx),%edx
@@ -270,10 +289,10 @@
 	/* prevent overflow */
 	subl	SIGL(%ebx),%eax
 	sbbl	SIGH(%ebx),%edx
-	movl	%edx,accum_2
-	movl	%eax,accum_1
+	movl	%edx,FPU_accum_2
+	movl	%eax,FPU_accum_1
 
-	incl	result_2	/* Reflect the subtraction in the answer */
+	incl	FPU_result_2	/* Reflect the subtraction in the answer */
 
 #ifdef PARANOID
 	je	L_bugged_2	/* Can't bump the result to 1.0 */
@@ -291,23 +310,23 @@
 	divl	%ecx		/* Divide the numerator by the denom ms dw */
 
 LSecond_div_done:
-	movl	%eax,result_1	/* Put the result in the answer */
+	movl	%eax,FPU_result_1	/* Put the result in the answer */
 
 	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
 
-	subl	%eax,accum_1	/* Subtract from the num local reg */
-	sbbl	%edx,accum_2
+	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_2
 
 #ifdef PARANOID
 	jc	L_bugged_2
 #endif PARANOID
 
-	movl	result_1,%eax	/* Get the result back */
+	movl	FPU_result_1,%eax	/* Get the result back */
 	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
 
-	subl	%eax,accum_0	/* Subtract from the num local reg */
-	sbbl	%edx,accum_1	/* Subtract from the num local reg */
-	sbbl	$0,accum_2
+	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_1	/* Subtract from the num local reg */
+	sbbl	$0,FPU_accum_2
 
 #ifdef PARANOID
 	jc	L_bugged_2
@@ -316,24 +335,24 @@
 	jz	LDo_3rd_32_bits
 
 #ifdef PARANOID
-	cmpl	$1,accum_2
+	cmpl	$1,FPU_accum_2
 	jne	L_bugged_2
 #endif PARANOID
 
 	/* need to subtract another once of the denom */
 	movl	SIGL(%ebx),%eax
 	movl	SIGH(%ebx),%edx
-	subl	%eax,accum_0	/* Subtract from the num local reg */
-	sbbl	%edx,accum_1
-	sbbl	$0,accum_2
+	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
+	sbbl	%edx,FPU_accum_1
+	sbbl	$0,FPU_accum_2
 
 #ifdef PARANOID
 	jc	L_bugged_2
 	jne	L_bugged_2
 #endif PARANOID
 
-	addl	$1,result_1	/* Correct the answer */
-	adcl	$0,result_2
+	addl	$1,FPU_result_1	/* Correct the answer */
+	adcl	$0,FPU_result_2
 
 #ifdef PARANOID
 	jc	L_bugged_2	/* Must check for non-zero result here */
@@ -341,11 +360,11 @@
 
 /*----------------------------------------------------------------------*/
 /* The division is essentially finished here, we just need to perform
-   tidying operations. */
-/* deal with the 3rd 32 bits */
+   tidying operations.
+   Deal with the 3rd 32 bits */
 LDo_3rd_32_bits:
-	movl	accum_1,%edx		/* get the reduced num */
-	movl	accum_0,%eax
+	movl	FPU_accum_1,%edx		/* get the reduced num */
+	movl	FPU_accum_0,%eax
 
 	/* need to check for possible subsequent overflow */
 	cmpl	SIGH(%ebx),%edx	/* denom */
@@ -359,16 +378,16 @@
 	/* prevent overflow */
 	subl	SIGL(%ebx),%eax
 	sbbl	SIGH(%ebx),%edx
-	movl	%edx,accum_1
-	movl	%eax,accum_0
+	movl	%edx,FPU_accum_1
+	movl	%eax,FPU_accum_0
 
-	addl	$1,result_1	/* Reflect the subtraction in the answer */
-	adcl	$0,result_2
+	addl	$1,FPU_result_1	/* Reflect the subtraction in the answer */
+	adcl	$0,FPU_result_2
 	jne	LRound_prep
 	jnc	LRound_prep
 
 	/* This is a tricky spot, there is an overflow of the answer */
-	movb	$255,ovfl_flag		/* Overflow -> 1.000 */
+	movb	$255,FPU_ovfl_flag		/* Overflow -> 1.000 */
 
 LRound_prep:
 /*
@@ -376,8 +395,8 @@
  * To test for rounding, we just need to compare 2*accum with the
  * denom.
  */
-	movl	accum_0,%ecx
-	movl	accum_1,%edx
+	movl	FPU_accum_0,%ecx
+	movl	FPU_accum_1,%edx
 	movl	%ecx,%eax
 	orl	%edx,%eax
 	jz	LRound_ovfl		/* The accumulator contains zero. */
@@ -407,15 +426,15 @@
 LRound_ovfl:
 /* We are now ready to deal with rounding, but first we must get
    the bits properly aligned */
-	testb	$255,ovfl_flag	/* was the num > denom ? */
+	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
 	je	LRound_precision
 
 	incl	EXP(%edi)
 
 	/* shift the mantissa right one bit */
 	stc			/* Will set the ms bit */
-	rcrl	result_2
-	rcrl	result_1
+	rcrl	FPU_result_2
+	rcrl	FPU_result_1
 	rcrl	%eax
 
 /* Round the result as required */
@@ -423,9 +442,9 @@
 	decl	EXP(%edi)	/* binary point between 1st & 2nd bits */
 
 	movl	%eax,%edx
-	movl	result_1,%ebx
-	movl	result_2,%eax
-	jmp	FPU_round
+	movl	FPU_result_1,%ebx
+	movl	FPU_result_2,%eax
+	jmp	fpu_reg_round
 
 
 #ifdef PARANOID
diff --git a/drivers/FPU-emu/reg_u_mul.S b/drivers/FPU-emu/reg_u_mul.S
index 8824176..d041bf3 100644
--- a/drivers/FPU-emu/reg_u_mul.S
+++ b/drivers/FPU-emu/reg_u_mul.S
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -26,12 +26,21 @@
 #include "control_w.h"
 
 
+
+#ifdef REENTRANT_FPU
+/*  Local storage on the stack: */
+#define FPU_accum_0	-4(%ebp)	/* ms word */
+#define FPU_accum_1	-8(%ebp)
+
+#else
+/*  Local storage in a static area: */
 .data
-	.align 2,0
-accum_0:
+	.align 4,0
+FPU_accum_0:
 	.long	0
-accum_1:
+FPU_accum_1:
 	.long	0
+#endif REENTRANT_FPU
 
 
 .text
@@ -41,6 +50,10 @@
 _reg_u_mul:
 	pushl	%ebp
 	movl	%esp,%ebp
+#ifdef REENTRANT_FPU
+	subl	$8,%esp
+#endif REENTRANT_FPU
+
 	pushl	%esi
 	pushl	%edi
 	pushl	%ebx
@@ -62,7 +75,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp1_not_denorm:
 	movl	EXP(%edi),%eax
@@ -71,7 +84,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp2_not_denorm:
 #endif DENORM_OPERAND
@@ -81,18 +94,18 @@
 
 	movl	SIGL(%esi),%eax
 	mull	SIGL(%edi)
-	movl	%eax,accum_0
-	movl	%edx,accum_1
+	movl	%eax,FPU_accum_0
+	movl	%edx,FPU_accum_1
 
 	movl	SIGL(%esi),%eax
 	mull	SIGH(%edi)
-	addl	%eax,accum_1
+	addl	%eax,FPU_accum_1
 	adcl	%edx,%ebx
 /*	adcl	$0,%ecx		// overflow here is not possible */
 
 	movl	SIGH(%esi),%eax
 	mull	SIGL(%edi)
-	addl	%eax,accum_1
+	addl	%eax,FPU_accum_1
 	adcl	%edx,%ebx
 	adcl	$0,%ecx
 
@@ -104,6 +117,7 @@
 	movl	EXP(%esi),%eax	/* Compute the exponent */
 	addl	EXP(%edi),%eax
 	subl	EXP_BIAS-1,%eax
+
 /*  Have now finished with the sources */
 	movl	PARAM3,%edi	/* Point to the destination */
 	movl	%eax,EXP(%edi)
@@ -113,15 +127,15 @@
 	jnz	LResult_Normalised
 
 	/* Normalize by shifting left one bit */
-	shll	$1,accum_0
-	rcll	$1,accum_1
+	shll	$1,FPU_accum_0
+	rcll	$1,FPU_accum_1
 	rcll	$1,%ebx
 	rcll	$1,%ecx
 	decl	EXP(%edi)
 
 LResult_Normalised:
-	movl	accum_0,%eax
-	movl	accum_1,%edx
+	movl	FPU_accum_0,%eax
+	movl	FPU_accum_1,%edx
 	orl	%eax,%eax
 	jz	L_extent_zero
 
@@ -129,7 +143,7 @@
 
 L_extent_zero:
 	movl	%ecx,%eax
-	jmp	FPU_round
+	jmp	fpu_reg_round
 
 
 #ifdef PARANOID
diff --git a/drivers/FPU-emu/reg_u_sub.S b/drivers/FPU-emu/reg_u_sub.S
index a7b5d89..fbec17d 100644
--- a/drivers/FPU-emu/reg_u_sub.S
+++ b/drivers/FPU-emu/reg_u_sub.S
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
@@ -48,7 +48,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp1_not_denorm:
 	cmpl	EXP_UNDER,EXP(%edi)
@@ -56,7 +56,7 @@
 
 	call	_denormal_operand
 	orl	%eax,%eax
-	jnz	FPU_Arith_exit
+	jnz	fpu_Arith_exit
 
 xOp2_not_denorm:
 #endif DENORM_OPERAND
@@ -85,8 +85,8 @@
 	movl	PARAM3,%edi	/* destination */
 	movl	EXP(%esi),%edx
 	movl	%edx,EXP(%edi)	/* Copy exponent to destination */
-/*	movb	SIGN(%esi),%dl */
-/*	movb	%dl,SIGN(%edi)	*/ /* Copy the sign from the first arg */
+/*	movb	SIGN(%esi),%dl
+	movb	%dl,SIGN(%edi) */	/* Copy the sign from the first arg */
 
 	xorl	%edx,%edx	/* register extension */
 
@@ -214,7 +214,7 @@
 	/* Shift left 64 bits */
 	subl	$64,EXP(%edi)
 	xchg	%edx,%eax
-	jmp	FPU_round
+	jmp	fpu_reg_round
 
 L_must_be_zero:
 #ifdef PARANOID
@@ -246,7 +246,7 @@
 	subl	%ecx,EXP(%edi)	/* Can get underflow here */
 
 L_round:
-	jmp	FPU_round	/* Round the result */
+	jmp	fpu_reg_round	/* Round the result */
 
 
 #ifdef PARANOID
diff --git a/drivers/FPU-emu/status_w.h b/drivers/FPU-emu/status_w.h
index 87521ff..96607d0 100644
--- a/drivers/FPU-emu/status_w.h
+++ b/drivers/FPU-emu/status_w.h
@@ -3,7 +3,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
diff --git a/drivers/FPU-emu/version.h b/drivers/FPU-emu/version.h
index ce22bab..587f364 100644
--- a/drivers/FPU-emu/version.h
+++ b/drivers/FPU-emu/version.h
@@ -4,7 +4,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
diff --git a/drivers/FPU-emu/wm_shrx.S b/drivers/FPU-emu/wm_shrx.S
index 59badea..bef0e19 100644
--- a/drivers/FPU-emu/wm_shrx.S
+++ b/drivers/FPU-emu/wm_shrx.S
@@ -5,7 +5,7 @@
  | 64 bit right shift functions                                              |
  |                                                                           |
  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   unsigned shrx(void *arg1, unsigned arg2)                                |
diff --git a/drivers/FPU-emu/wm_sqrt.S b/drivers/FPU-emu/wm_sqrt.S
index bc53aa5..7f8b6c6 100644
--- a/drivers/FPU-emu/wm_sqrt.S
+++ b/drivers/FPU-emu/wm_sqrt.S
@@ -6,7 +6,7 @@
  |                                                                           |
  | Copyright (C) 1992,1993                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
  | Call from C as:                                                           |
  |   void wm_sqrt(FPU_REG *n, unsigned int control_word)                     |
@@ -29,19 +29,12 @@
 #include "fpu_asm.h"
 
 
-.data
-/*
-	Local storage:
- */
-	.align 4,0
-accum_3:
-	.long	0		/* ms word */
-accum_2:
-	.long	0
-accum_1:
-	.long	0
-accum_0:
-	.long	0
+#ifdef REENTRANT_FPU
+/*	Local storage on the stack: */
+#define FPU_accum_3	-4(%ebp)	/* ms word */
+#define FPU_accum_2	-8(%ebp)
+#define FPU_accum_1	-12(%ebp)
+#define FPU_accum_0	-16(%ebp)
 
 /*
  * The de-normalised argument:
@@ -49,21 +42,47 @@
  *        b b b b b b b ... b b b   b b b .... b b b   b 0 0 0 ... 0
  *           ^ binary point here
  */
-fsqrt_arg_2:
+#define FPU_fsqrt_arg_2	-20(%ebp)	/* ms word */
+#define FPU_fsqrt_arg_1	-24(%ebp)
+#define FPU_fsqrt_arg_0	-28(%ebp)	/* ls word, at most the ms bit is set */
+
+#else
+/*	Local storage in a static area: */
+.data
+	.align 4,0
+FPU_accum_3:
 	.long	0		/* ms word */
-fsqrt_arg_1:
+FPU_accum_2:
 	.long	0
-fsqrt_arg_0:
+FPU_accum_1:
+	.long	0
+FPU_accum_0:
+	.long	0
+
+/* The de-normalised argument:
+                    sq_2                  sq_1              sq_0
+          b b b b b b b ... b b b   b b b .... b b b   b 0 0 0 ... 0
+             ^ binary point here
+ */
+FPU_fsqrt_arg_2:
+	.long	0		/* ms word */
+FPU_fsqrt_arg_1:
+	.long	0
+FPU_fsqrt_arg_0:
 	.long	0		/* ls word, at most the ms bit is set */
+#endif REENTRANT_FPU
+
 
 .text
 	.align 2,144
 
 .globl _wm_sqrt
-
 _wm_sqrt:
 	pushl	%ebp
 	movl	%esp,%ebp
+#ifdef REENTRANT_FPU
+	subl	$28,%esp
+#endif REENTRANT_FPU
 	pushl	%esi
 	pushl	%edi
 	pushl	%ebx
@@ -84,12 +103,12 @@
 	rcrl	$1,%edx
 
 sqrt_arg_ge_2:
-/* From here on, n is never accessed directly again until it is */
-/* replaced by the answer. */
+/* From here on, n is never accessed directly again until it is
+   replaced by the answer. */
 
-	movl	%eax,fsqrt_arg_2		/* ms word of n */
-	movl	%ecx,fsqrt_arg_1
-	movl	%edx,fsqrt_arg_0
+	movl	%eax,FPU_fsqrt_arg_2		/* ms word of n */
+	movl	%ecx,FPU_fsqrt_arg_1
+	movl	%edx,FPU_fsqrt_arg_0
 
 /* Make a linear first estimate */
 	shrl	$1,%eax
@@ -108,7 +127,7 @@
 /* We have now computed (approx)   (2 + x) / 3, which forms the basis
    for a few iterations of Newton's method */
 
-	movl	fsqrt_arg_2,%ecx	/* ms word */
+	movl	FPU_fsqrt_arg_2,%ecx	/* ms word */
 
 /*
  * From our initial estimate, three iterations are enough to get us
@@ -148,14 +167,14 @@
 	mull	%esi
 /* guess^2 now in %edx:%eax */
 
-	movl	fsqrt_arg_1,%ecx
+	movl	FPU_fsqrt_arg_1,%ecx
 	subl	%ecx,%eax
-	movl	fsqrt_arg_2,%ecx	/* ms word of normalized n */
+	movl	FPU_fsqrt_arg_2,%ecx	/* ms word of normalized n */
 	sbbl	%ecx,%edx
 	jnc	sqrt_stage_2_positive
 
-/* subtraction gives a negative result */
-/* negate the result before division */
+/* Subtraction gives a negative result,
+   negate the result before division. */
 	notl	%edx
 	notl	%eax
 	addl	$1,%eax
@@ -192,7 +211,7 @@
 
 #ifdef PARANOID
 /* It should be possible to get here only if the arg is ffff....ffff */
-	cmp	$0xffffffff,fsqrt_arg_1
+	cmp	$0xffffffff,FPU_fsqrt_arg_1
 	jnz	sqrt_stage_2_error
 #endif PARANOID
 
@@ -212,50 +231,50 @@
 
 sqrt_stage_2_done:
 
-/* Now the square root has been computed to better than 60 bits */
+/* Now the square root has been computed to better than 60 bits. */
 
-/* Find the square of the guess */
+/* Find the square of the guess. */
 	movl	%edi,%eax		/* ls word of guess */
 	mull	%edi
-	movl	%edx,accum_1
+	movl	%edx,FPU_accum_1
 
 	movl	%esi,%eax
 	mull	%esi
-	movl	%edx,accum_3
-	movl	%eax,accum_2
+	movl	%edx,FPU_accum_3
+	movl	%eax,FPU_accum_2
 
 	movl	%edi,%eax
 	mull	%esi
-	addl	%eax,accum_1
-	adcl	%edx,accum_2
-	adcl	$0,accum_3
+	addl	%eax,FPU_accum_1
+	adcl	%edx,FPU_accum_2
+	adcl	$0,FPU_accum_3
 
 /*	movl	%esi,%eax */
 /*	mull	%edi */
-	addl	%eax,accum_1
-	adcl	%edx,accum_2
-	adcl	$0,accum_3
+	addl	%eax,FPU_accum_1
+	adcl	%edx,FPU_accum_2
+	adcl	$0,FPU_accum_3
 
-/* guess^2 now in accum_3:accum_2:accum_1 */
+/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
 
-	movl	fsqrt_arg_0,%eax		/* get normalized n */
-	subl	%eax,accum_1
-	movl	fsqrt_arg_1,%eax
-	sbbl	%eax,accum_2
-	movl	fsqrt_arg_2,%eax		/* ms word of normalized n */
-	sbbl	%eax,accum_3
+	movl	FPU_fsqrt_arg_0,%eax		/* get normalized n */
+	subl	%eax,FPU_accum_1
+	movl	FPU_fsqrt_arg_1,%eax
+	sbbl	%eax,FPU_accum_2
+	movl	FPU_fsqrt_arg_2,%eax		/* ms word of normalized n */
+	sbbl	%eax,FPU_accum_3
 	jnc	sqrt_stage_3_positive
 
-/* subtraction gives a negative result */
-/* negate the result before division */
-	notl	accum_1
-	notl	accum_2
-	notl	accum_3
-	addl	$1,accum_1
-	adcl	$0,accum_2
+/* Subtraction gives a negative result,
+   negate the result before division */
+	notl	FPU_accum_1
+	notl	FPU_accum_2
+	notl	FPU_accum_3
+	addl	$1,FPU_accum_1
+	adcl	$0,FPU_accum_2
 
 #ifdef PARANOID
-	adcl	$0,accum_3	/* This must be zero */
+	adcl	$0,FPU_accum_3	/* This must be zero */
 	jz	sqrt_stage_3_no_error
 
 sqrt_stage_3_error:
@@ -265,8 +284,8 @@
 sqrt_stage_3_no_error:
 #endif PARANOID
 
-	movl	accum_2,%edx
-	movl	accum_1,%eax
+	movl	FPU_accum_2,%edx
+	movl	FPU_accum_1,%eax
 	divl	%esi
 	movl	%eax,%ecx
 
@@ -284,8 +303,8 @@
 	jmp	sqrt_stage_3_finished
 
 sqrt_stage_3_positive:
-	movl	accum_2,%edx
-	movl	accum_1,%eax
+	movl	FPU_accum_2,%edx
+	movl	FPU_accum_1,%eax
 	divl	%esi
 	movl	%eax,%ecx
 
@@ -333,7 +352,7 @@
 	movl	PARAM1,%edi
 	movl	EXP_BIAS,EXP(%edi)	/* Result is in  [1.0 .. 2.0) */
 	movl	PARAM2,%ecx
-	jmp	FPU_round_sqrt
+	jmp	fpu_reg_round_sqrt
 
 
 sqrt_near_exact_x:
@@ -398,8 +417,8 @@
 
 
 sqrt_get_more_precision:
-/* This case is almost the same as the above, except we start */
-/* with an extra bit of precision in the estimate. */
+/* This case is almost the same as the above, except we start
+   with an extra bit of precision in the estimate. */
 	stc			/* The extra bit. */
 	rcll	$1,%edi		/* Shift the estimate left one bit */
 	rcll	$1,%esi
diff --git a/drivers/Makefile b/drivers/Makefile
index fdb28d2..a30e4a7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -33,14 +33,10 @@
 all: driversubdirs
 
 driversubdirs: dummy
-	@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
-
-clean:
-	rm -f core *.o *.a *.s
-	for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
 
 dep:
-	for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
 
 dummy:
 
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index ab80be1..253de65 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -51,10 +51,6 @@
 	$(AR) rcs block.a $(OBJS)
 	sync
 
-clean: 
-	rm -f core *.o *.a *.s
-	for i in $(ALL_SUBDIRS); do (cd $$i && $(MAKE) clean); done
-
 dep:
 	$(CPP) -M $(SRCS) > .depend
 
diff --git a/drivers/block/blk.h b/drivers/block/blk.h
index 0f5cb8f..970ba4d 100644
--- a/drivers/block/blk.h
+++ b/drivers/block/blk.h
@@ -1,6 +1,7 @@
 #ifndef _BLK_H
 #define _BLK_H
 
+#include <linux/major.h>
 #include <linux/sched.h>
 #include <linux/locks.h>
 #include <linux/genhd.h>
@@ -15,7 +16,7 @@
  * buffers when they are in the queue. 64 seems to be too many (easily
  * long pauses in reading when heavy writing/syncing is going on)
  */
-#define NR_REQUEST	32
+#define NR_REQUEST	64
 
 /*
  * Ok, this is an expanded form so that we can use the same
@@ -71,7 +72,6 @@
 
 extern struct sec_size * blk_sec[MAX_BLKDEV];
 extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
-extern struct request request[NR_REQUEST];
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 
@@ -104,7 +104,8 @@
  * supported are hard-disks and floppies.
  */
 
-#if (MAJOR_NR == 1)
+#if (MAJOR_NR == MEM_MAJOR)
+
 /* ram disk */
 #define DEVICE_NAME "ramdisk"
 #define DEVICE_REQUEST do_rd_request
@@ -112,8 +113,8 @@
 #define DEVICE_ON(device) 
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 2)
-/* floppy */
+#elif (MAJOR_NR == FLOPPY_MAJOR)
+
 static void floppy_on(unsigned int nr);
 static void floppy_off(unsigned int nr);
 
@@ -124,7 +125,8 @@
 #define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
 #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
 
-#elif (MAJOR_NR == 3)
+#elif (MAJOR_NR == HD_MAJOR)
+
 /* harddisk: timeout is 6 seconds.. */
 #define DEVICE_NAME "harddisk"
 #define DEVICE_INTR do_hd
@@ -135,8 +137,8 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 8)
-/* scsi disk */
+#elif (MAJOR_NR == SCSI_DISK_MAJOR)
+
 #define DEVICE_NAME "scsidisk"
 #define DEVICE_INTR do_sd  
 #define TIMEOUT_VALUE 200
@@ -145,17 +147,16 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 9)
-/* scsi tape */
+#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
+
 #define DEVICE_NAME "scsitape"
 #define DEVICE_INTR do_st  
-#define DEVICE_REQUEST do_st_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 11)
-/* scsi CD-ROM */
+#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
+
 #define DEVICE_NAME "CD-ROM"
 #define DEVICE_INTR do_sr
 #define DEVICE_REQUEST do_sr_request
@@ -163,24 +164,24 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 13)
-/* xt hard disk */
+#elif (MAJOR_NR == XT_DISK_MAJOR)
+
 #define DEVICE_NAME "xt disk"
 #define DEVICE_REQUEST do_xd_request
 #define DEVICE_NR(device) (MINOR(device) >> 6)
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 15)
-/* CDU31A CD-ROM */
+#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
+
 #define DEVICE_NAME "CDU31A"
 #define DEVICE_REQUEST do_cdu31a_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == 23)
-/* MITSUMI CD-ROM */
+#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
+
 #define DEVICE_NAME "Mitsumi CD-ROM"
 /* #define DEVICE_INTR do_mcd */
 #define DEVICE_REQUEST do_mcd_request
@@ -189,12 +190,12 @@
 #define DEVICE_OFF(device)
 
 #else
-/* unknown blk device */
+
 #error "unknown blk device"
 
 #endif
 
-#if (MAJOR_NR != 9)
+#if (MAJOR_NR != SCSI_TAPE_MAJOR)
 
 #ifndef CURRENT
 #define CURRENT (blk_dev[MAJOR_NR].current_request)
@@ -227,8 +228,10 @@
 #endif
 static void (DEVICE_REQUEST)(void);
 
-/* SCSI devices have their own version */
-#if (MAJOR_NR != 8 && MAJOR_NR != 9 && MAJOR_NR != 11)
+/* end_request() - SCSI devices have their own version */
+
+#if ! SCSI_MAJOR(MAJOR_NR)
+
 static void end_request(int uptodate)
 {
 	struct request * req;
@@ -239,7 +242,8 @@
 	req->errors = 0;
 	if (!uptodate) {
 		printk(DEVICE_NAME " I/O error\n");
-		printk("dev %04x, sector %d\n",req->dev,req->sector);
+		printk("dev %04lX, sector %lu\n",
+		       (unsigned long)req->dev, req->sector);
 		req->nr_sectors--;
 		req->nr_sectors &= ~SECTOR_MASK;
 		req->sector += (BLOCK_SIZE / 512);
diff --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c
index 64d90fc..4a56d16 100644
--- a/drivers/block/cdu31a.c
+++ b/drivers/block/cdu31a.c
@@ -68,6 +68,7 @@
 #include <linux/kernel.h>
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
+#include <linux/ioport.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -76,7 +77,7 @@
 #include <linux/cdrom.h>
 #include <linux/cdu31a.h>
 
-#define MAJOR_NR 15
+#define MAJOR_NR CDU31A_CDROM_MAJOR
 #include "blk.h"
 
 
@@ -616,6 +617,9 @@
        && (num_retries < MAX_CDU31A_RETRIES))
    {
       num_retries++;
+      current->state = TASK_INTERRUPTIBLE;
+      current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
+      schedule();
       goto retry_data_operation;
    }
 
@@ -693,6 +697,9 @@
        && (num_retries < MAX_CDU31A_RETRIES))
    {
       num_retries++;
+      current->state = TASK_INTERRUPTIBLE;
+      current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
+      schedule();
       goto retry_cd_operation;
    }
 
@@ -1129,8 +1136,8 @@
 static int
 scd_ioctl(struct inode *inode,
           struct file  *file,
-          unsigned int cmd,
-          unsigned int arg)
+          unsigned int  cmd,
+          unsigned long arg)
 {
    unsigned int dev;
    unsigned char res_reg[2];
@@ -1449,10 +1456,14 @@
 {
    unsigned char res_reg[2];
    unsigned int res_size;
+   int num_spin_ups;
 
 
    if (!sony_spun_up)
    {
+      num_spin_ups = 0;
+
+respinup_on_open:
       do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
 
       /* The drive sometimes returns error 0.  I don't know why, but ignore
@@ -1475,6 +1486,15 @@
             goto drive_spinning;
          }
 
+         /* If the drive says it is not spun up (even though we just did it!)
+            then retry the operation at least a few times. */
+         if (   (res_reg[1] == SONY_NOT_SPIN_ERR)
+             && (num_spin_ups < MAX_CDU31A_RETRIES))
+         {
+            num_spin_ups++;
+            goto respinup_on_open;
+         }
+
          printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
          do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
          
@@ -1539,7 +1559,8 @@
    scd_ioctl,              /* ioctl */
    NULL,                   /* mmap */
    scd_open,               /* open */
-   scd_release             /* release */
+   scd_release,            /* release */
+   NULL                    /* fsync */
 };
 
 
@@ -1641,12 +1662,17 @@
    while (   (cdu31a_addresses[i] != 0)
           && (!drive_found))
    {
+      if (check_region(cdu31a_addresses[i], 4)) {
+	  i++;
+	  continue;
+      }
       get_drive_configuration(cdu31a_addresses[i],
                                drive_config.exec_status,
                                &res_size);
       if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00))
       {
          drive_found = 1;
+	 snarf_region(cdu31a_addresses[i], 4);
 
          if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
          {
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 493571e..37eb05d 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -81,7 +81,7 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 
-#define MAJOR_NR 2
+#define MAJOR_NR FLOPPY_MAJOR
 #include "blk.h"
 
 static unsigned int changed_floppies = 0, fake_change = 0;
@@ -393,17 +393,19 @@
 {
 	unsigned int mask = 1 << (bh->b_dev & 0x03);
 
-	if (MAJOR(bh->b_dev) != 2) {
+	if (MAJOR(bh->b_dev) != MAJOR_NR) {
 		printk("floppy_changed: not a floppy\n");
 		return 0;
 	}
 	if (fake_change & mask) {
+		buffer_track = -1;
 		fake_change &= ~mask;
 /* omitting the next line breaks formatting in a horrible way ... */
 		changed_floppies &= ~mask;
 		return 1;
 	}
 	if (changed_floppies & mask) {
+		buffer_track = -1;
 		changed_floppies &= ~mask;
 		recalibrate = 1;
 		return 1;
@@ -453,7 +455,7 @@
 	if (read_track) {
 /* mark buffer-track bad, in case all this fails.. */
 		buffer_drive = buffer_track = -1;
-		count = floppy->sect*2*512;
+		count = floppy->sect*floppy->head*512;
 		addr = (long) floppy_track_buffer;
 	} else if (addr >= LAST_DMA_ADDR) {
 		addr = (long) tmp_floppy_area;
@@ -1166,6 +1168,7 @@
 			sti();
 			okay = format_status == FORMAT_OKAY;
 			format_status = FORMAT_NONE;
+			floppy_off(drive & 3);
 			wake_up(&format_done);
 			return okay ? 0 : -EIO;
 		case FDFLUSH:
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 5651176..b4dd66a 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -17,8 +17,6 @@
  */
 
 
-#define HD_IRQ 14
-
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -34,9 +32,11 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 
-#define MAJOR_NR 3
+#define MAJOR_NR HD_MAJOR
 #include "blk.h"
 
+#define HD_IRQ 14
+
 static int revalidate_hddisk(int, int);
 
 static inline unsigned char CMOS_READ(unsigned char addr)
@@ -121,6 +121,7 @@
 	hd_info[hdind].wpcom = 0;
 	hd_info[hdind].lzone = ints[1];
 	hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+	NR_HD = hdind+1;
 }
 
 static int win_result(void)
@@ -642,20 +643,20 @@
 static void hd_geninit(void)
 {
 	int drive, i;
-#ifndef HD_TYPE
 	extern struct drive_info drive_info;
-	void *BIOS = (void *) &drive_info;
+	unsigned char *BIOS = (unsigned char *) &drive_info;
 	int cmos_disks;
-	   
-	for (drive=0 ; drive<2 ; drive++) {
-		hd_info[drive].cyl = *(unsigned short *) BIOS;
-		hd_info[drive].head = *(unsigned char *) (2+BIOS);
-		hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
-		hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
-		hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
-		hd_info[drive].sect = *(unsigned char *) (14+BIOS);
-		BIOS += 16;
-	}
+
+	if (!NR_HD) {	   
+		for (drive=0 ; drive<2 ; drive++) {
+			hd_info[drive].cyl = *(unsigned short *) BIOS;
+			hd_info[drive].head = *(2+BIOS);
+			hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
+			hd_info[drive].ctl = *(8+BIOS);
+			hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
+			hd_info[drive].sect = *(14+BIOS);
+			BIOS += 16;
+		}
 
 	/*
 		We querry CMOS about hard disks : it could be that 
@@ -679,14 +680,12 @@
 		
 	*/
 
-	if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
-		if (cmos_disks & 0x0f)
-			NR_HD = 2;
-		else
-			NR_HD = 1;
-	else
-		NR_HD = 0;
-#endif
+		if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
+			if (cmos_disks & 0x0f)
+				NR_HD = 2;
+			else
+				NR_HD = 1;
+	}
 	i = NR_HD;
 	while (i-- > 0) {
 		hd[i<<6].nr_sects = 0;
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 7ffcd76..1473621 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -22,7 +22,7 @@
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
  */
-struct request request[NR_REQUEST];
+static struct request all_requests[NR_REQUEST];
 
 /*
  * used to wait on when there are no free requests
@@ -69,6 +69,51 @@
  */
 int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
 
+/*
+ * look for a free request in the first N entries.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request(int n, int dev)
+{
+	static struct request *prev_found = NULL, *prev_limit = NULL;
+	register struct request *req, *limit;
+
+	if (n <= 0)
+		panic("get_request(%d): impossible!\n", n);
+
+	limit = all_requests + n;
+	if (limit != prev_limit) {
+		prev_limit = limit;
+		prev_found = all_requests;
+	}
+	req = prev_found;
+	for (;;) {
+		req = ((req > all_requests) ? req : limit) - 1;
+		if (req->dev < 0)
+			break;
+		if (req == prev_found)
+			return NULL;
+	}
+	prev_found = req;
+	req->dev = dev;
+	return req;
+}
+
+/*
+ * wait until a free request in the first N entries is available.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request_wait(int n, int dev)
+{
+	register struct request *req;
+
+	while ((req = get_request(n, dev)) == NULL)
+		sleep_on(&wait_for_request);
+	return req;
+}
+
 /* RO fail safe mechanism */
 
 static long ro_bits[MAX_BLKDEV][8];
@@ -122,11 +167,9 @@
 	req->next = tmp->next;
 	tmp->next = req;
 
-/* Scsi devices are treated differently */
-	if(MAJOR(req->dev) == 8 || 
-	   MAJOR(req->dev) == 9 ||
-	   MAJOR(req->dev) == 11)
-	  (dev->request_fn)();
+/* for SCSI devices, call request_fn unconditionally */
+	if (scsi_major(MAJOR(req->dev)))
+		(dev->request_fn)();
 
 	sti();
 }
@@ -135,7 +178,7 @@
 {
 	unsigned int sector, count;
 	struct request * req;
-	int rw_ahead;
+	int rw_ahead, max_req;
 
 /* WRITEA/READA is special case - it is not really needed, so if the */
 /* buffer is locked, we just forget about it, else it's a normal read */
@@ -164,33 +207,50 @@
 		unlock_buffer(bh);
 		return;
 	}
-/* The scsi disk drivers completely remove the request from the queue when
-   they start processing an entry.  For this reason it is safe to continue
-   to add links to the top entry for scsi devices */
+
+/* we don't allow the write-requests to fill up the queue completely:
+ * we want some room for reads: they take precedence. The last third
+ * of the requests are only for reads.
+ */
+	max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+
+/* big loop: look for a free request. */
 
 repeat:
 	cli();
-	if ((major == 3 ||  major == 8 || major == 11)&& (req = blk_dev[major].current_request)) {
-	        if(major == 3) req = req->next;
+
+/* The scsi disk drivers completely remove the request from the queue when
+ * they start processing an entry.  For this reason it is safe to continue
+ * to add links to the top entry for scsi devices.
+ */
+	if ((major == HD_MAJOR
+	     || major == SCSI_DISK_MAJOR
+	     || major == SCSI_CDROM_MAJOR)
+	    && (req = blk_dev[major].current_request))
+	{
+	        if (major == HD_MAJOR)
+			req = req->next;
 		while (req) {
 			if (req->dev == bh->b_dev &&
 			    !req->waiting &&
 			    req->cmd == rw &&
 			    req->sector + req->nr_sectors == sector &&
-			    req->nr_sectors < 254) {
+			    req->nr_sectors < 254)
+			{
 				req->bhtail->b_reqnext = bh;
 				req->bhtail = bh;
 				req->nr_sectors += count;
 				bh->b_dirt = 0;
 				sti();
 				return;
-			      }
-			else if ( req->dev == bh->b_dev &&
+			}
+
+			if (req->dev == bh->b_dev &&
 			    !req->waiting &&
 			    req->cmd == rw &&
 			    req->sector - count == sector &&
 			    req->nr_sectors < 254)
-			    	{
+			{
 			    	req->nr_sectors += count;
 			    	bh->b_reqnext = req->bh;
 			    	req->buffer = bh->b_data;
@@ -200,36 +260,31 @@
 			    	req->bh = bh;
 			    	sti();
 			    	return;
-			    }    
-			req = req->next;
-		      }
-	      }
-/* we don't allow the write-requests to fill up the queue completely:
- * we want some room for reads: they take precedence. The last third
- * of the requests are only for reads.
- */
-	if (rw == READ)
-		req = request+NR_REQUEST;
-	else
-		req = request+(NR_REQUEST/2);
-/* find an empty request */
-	while (--req >= request)
-		if (req->dev < 0)
-			goto found;
-/* if none found, sleep on new requests: check for rw_ahead */
-	if (rw_ahead) {
-		sti();
-		unlock_buffer(bh);
-		return;
-	}
-	sleep_on(&wait_for_request);
-	sti();
-	goto repeat;
+			}    
 
-found:
-/* fill up the request-info, and add it to the queue */
-	req->dev = bh->b_dev;
+			req = req->next;
+		}
+	}
+
+/* find an unused request. */
+	req = get_request(max_req, bh->b_dev);
+
+/* if no request available: if rw_ahead, forget it; otherwise try again. */
+	if (! req) {
+		if (rw_ahead) {
+			sti();
+			unlock_buffer(bh);
+			return;
+		}
+		sleep_on(&wait_for_request);
+		sti();
+		goto repeat;
+	}
+
+/* we found a request. */
 	sti();
+
+/* fill up the request-info, and add it to the queue */
 	req->cmd = rw;
 	req->errors = 0;
 	req->sector = sector;
@@ -258,19 +313,10 @@
 		printk("Can't page to read-only device 0x%X\n",dev);
 		return;
 	}
-repeat:
 	cli();
-	req = request+NR_REQUEST;
-	while (--req >= request)
-		if (req->dev<0)
-			break;
-	if (req < request) {
-		sleep_on(&wait_for_request);
-		goto repeat;
-	}
-/* fill up the request-info, and add it to the queue */
-	req->dev = dev;
+	req = get_request_wait(NR_REQUEST, dev);
 	sti();
+/* fill up the request-info, and add it to the queue */
 	req->cmd = rw;
 	req->errors = 0;
 	req->sector = page<<3;
@@ -292,77 +338,86 @@
 void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
 {
 	unsigned int major;
-
 	struct request plug;
 	int plugged;
 	int correct_size;
 	struct blk_dev_struct * dev;
-	int i, j;
+	int i;
 
 	/* Make sure that the first block contains something reasonable */
-	while(!bh[0]){
-	  bh++;
-	  nr--;
-	  if (nr <= 0) return;
+	while (!*bh) {
+		bh++;
+		if (--nr <= 0)
+			return;
 	};
 
-	if ((major=MAJOR(bh[0]->b_dev)) >= MAX_BLKDEV ||
-	!(blk_dev[major].request_fn)) {
-		printk("ll_rw_block: Trying to read nonexistent block-device %04x (%d)\n",bh[0]->b_dev,bh[0]->b_blocknr);
-		for (i=0;i<nr; i++)
-		  if (bh[i]) bh[i]->b_dirt = bh[i]->b_uptodate = 0;
-		return;
+	dev = NULL;
+	if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
+		dev = blk_dev + major;
+	if (!dev || !dev->request_fn) {
+		printk(
+	"ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
+		       (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
+		goto sorry;
 	}
 
-	for(j=0;j<nr; j++){
-	  if(!bh[j]) continue;
-	  /* Determine correct block size for this device */
-	  correct_size = BLOCK_SIZE;
-	  if(blksize_size[major] && blksize_size[major][MINOR(bh[j]->b_dev)])
-	    correct_size = blksize_size[major][MINOR(bh[j]->b_dev)];
-	  
-	  if(bh[j]->b_size != correct_size) {
-	    
-	    printk("ll_rw_block: only %d-char blocks implemented (%d)\n",
-		   correct_size, bh[j]->b_size);
-	    
-	    for (i=0;i<nr; i++)
-	      if (bh[i]) bh[i]->b_dirt = bh[i]->b_uptodate = 0;
-	    return;
-	  }
-	};
+	/* Determine correct block size for this device.  */
+	correct_size = BLOCK_SIZE;
+	if (blksize_size[major]) {
+		i = blksize_size[major][MINOR(bh[0]->b_dev)];
+		if (i)
+			correct_size = i;
+	}
+
+	/* Verify requested block sizees.  */
+	for (i = 0; i < nr; i++) {
+		if (bh[i] && bh[i]->b_size != correct_size) {
+			printk(
+			"ll_rw_block: only %d-char blocks implemented (%lu)\n",
+			       correct_size, bh[i]->b_size);
+			goto sorry;
+		}
+	}
 
 	if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
 		printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
-		for (i=0;i<nr; i++)
-		  if (bh[i]) bh[i]->b_dirt = bh[i]->b_uptodate = 0;
-		return;
+		goto sorry;
 	}
-/* If there are no pending requests for this device, then we insert a dummy
-   request for that device.  This will prevent the request from starting until
-   we have shoved all of the blocks into the queue, and then we let it rip */
+
+	/* If there are no pending requests for this device, then we insert
+	   a dummy request for that device.  This will prevent the request
+	   from starting until we have shoved all of the blocks into the
+	   queue, and then we let it rip.  */
 
 	plugged = 0;
 	cli();
-	if (!blk_dev[major].current_request && nr > 1) {
-	  blk_dev[major].current_request = &plug;
-	  plug.dev = -1;
-	  plug.next = NULL;
-	  plugged = 1;
-	};
-	sti();
-	for (i=0;i<nr; i++)
-	  if (bh[i]) {
-	    bh[i]->b_req = 1;
-	    make_request(major, rw, bh[i]);
+	if (!dev->current_request && nr > 1) {
+		dev->current_request = &plug;
+		plug.dev = -1;
+		plug.next = NULL;
+		plugged = 1;
 	}
-	if(plugged){
-	  cli();
-	  blk_dev[major].current_request = plug.next;
-	  dev = major+blk_dev;
-	  (dev->request_fn)();
-	  sti();
-	};
+	sti();
+	for (i = 0; i < nr; i++) {
+		if (bh[i]) {
+			bh[i]->b_req = 1;
+			make_request(major, rw, bh[i]);
+		}
+	}
+	if (plugged) {
+		cli();
+		dev->current_request = plug.next;
+		(dev->request_fn)();
+		sti();
+	}
+	return;
+
+      sorry:
+	for (i = 0; i < nr; i++) {
+		if (bh[i])
+			bh[i]->b_dirt = bh[i]->b_uptodate = 0;
+	}
+	return;
 }
 
 void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
@@ -390,17 +445,8 @@
 
 	for (i=0; i<nb; i++, buf += buffersize)
 	{
-repeat:
 		cli();
-		req = request+NR_REQUEST;
-		while (--req >= request)
-			if (req->dev<0)
-				break;
-		if (req < request) {
-			sleep_on(&wait_for_request);
-			goto repeat;
-		}
-		req->dev = dev;
+		req = get_request_wait(NR_REQUEST, dev);
 		sti();
 		req->cmd = rw;
 		req->errors = 0;
@@ -419,11 +465,12 @@
 
 long blk_dev_init(long mem_start, long mem_end)
 {
-	int i;
+	struct request * req;
 
-	for (i=0 ; i<NR_REQUEST ; i++) {
-		request[i].dev = -1;
-		request[i].next = NULL;
+	req = all_requests + NR_REQUEST;
+	while (--req >= all_requests) {
+		req->dev = -1;
+		req->next = NULL;
 	}
 	memset(ro_bits,0,sizeof(ro_bits));
 #ifdef CONFIG_BLK_DEV_HD
diff --git a/drivers/block/mcd.c b/drivers/block/mcd.c
index e6a6206..50e9c31 100644
--- a/drivers/block/mcd.c
+++ b/drivers/block/mcd.c
@@ -44,8 +44,9 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 
-#define MAJOR_NR 23
+#define MAJOR_NR MITSUMI_CDROM_MAJOR
 #include "blk.h"
+
 #include <linux/mcd.h>
 
 #if 0
diff --git a/drivers/block/ramdisk.c b/drivers/block/ramdisk.c
index 2fd4343..4904409 100644
--- a/drivers/block/ramdisk.c
+++ b/drivers/block/ramdisk.c
@@ -17,13 +17,11 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 
-#define MAJOR_RAMDISK	1		/* should be in <linux/major.h>	*/
-#define MAJOR_FLOPPY	2		/* should be in <linux/major.h>	*/
-#define MINOR_RAMDISK	1
-
-#define MAJOR_NR	MAJOR_RAMDISK	/* weird hack- FvK */
+#define MAJOR_NR  MEM_MAJOR
 #include "blk.h"
 
+#define RAMDISK_MINOR	1
+
 
 char	*rd_start;
 int	rd_length = 0;
@@ -39,7 +37,7 @@
 	addr = rd_start + (CURRENT->sector << 9);
 	len = CURRENT->current_nr_sectors << 9;
 
-	if ((MINOR(CURRENT->dev) != MINOR_RAMDISK) ||
+	if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
 	    (addr+len > rd_start+rd_length)) {
 		end_request(0);
 		goto repeat;
@@ -79,11 +77,11 @@
 	int	i;
 	char	*cp;
 
-	if (register_blkdev(MAJOR_RAMDISK,"rd",&rd_fops)) {
-		printk("RAMDISK: Unable to get major %d.\n", MAJOR_RAMDISK);
+	if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
+		printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
 		return 0;
 	}
-	blk_dev[MAJOR_RAMDISK].request_fn = DEVICE_REQUEST;
+	blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
 	rd_start = (char *) mem_start;
 	rd_length = length;
 	cp = rd_start;
@@ -116,7 +114,7 @@
 					rd_length, (int) rd_start);
 
 	/* If we are doing a diskette boot, we might have to pre-load it. */
-	if (MAJOR(ROOT_DEV) != MAJOR_FLOPPY) return;
+	if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
 
 	/*
 	 * Check for a super block on the diskette.
@@ -174,7 +172,7 @@
 		printk("\ndone\n");
 
 		/* We loaded the file system image.  Prepare for mounting it. */
-		ROOT_DEV = ((MAJOR_RAMDISK << 8) | MINOR_RAMDISK);
+		ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
 		return;
 	}
 }
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index a2fe529..07a4a42 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -23,7 +23,7 @@
 #include <asm/segment.h>
 #include <asm/dma.h>
 
-#define MAJOR_NR 13
+#define MAJOR_NR XT_DISK_MAJOR
 #include "blk.h"
 
 XD_INFO xd_info[XD_MAXDRIVES];
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 52651bb..b72485c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -72,14 +72,7 @@
 
 char.a: $(OBJS)
 	$(AR) rcs char.a $(OBJS)
-	sync
-
-defkeymap.c: defkeymap.map
-	./loadkeys defkeymap.map
-	./mktable	> defkeymap.c
-
-clean: 
-	rm -f core *.o *.a *.s
+	sync	
 
 dep:
 	$(CPP) -M $(SRCS) > .depend
diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c
index 79703b4..6e3792c 100644
--- a/drivers/char/busmouse.c
+++ b/drivers/char/busmouse.c
@@ -2,6 +2,10 @@
  * Logitech Bus Mouse Driver for Linux
  * by James Banks
  *
+ * Mods by Matthew Dillon
+ *   calls verify_area()
+ *   tracks better when X is busy or paging
+ *
  * Heavily modified by David Giller
  *   changed from queue- to counter- driven
  *   hacked out a (probably incorrect) mouse_select
@@ -59,83 +63,143 @@
 	if (dx != 0 || dy != 0 || buttons != mouse.buttons) {
 	  mouse.buttons = buttons;
 	  mouse.dx += dx;
-	  mouse.dy += dy;
+	  mouse.dy -= dy;
 	  mouse.ready = 1;
 	  wake_up_interruptible(&mouse.wait);
+
+	  /*
+	   * keep dx/dy reasonable, but still able to track when X (or
+	   * whatever) must page or is busy (i.e. long waits between
+	   * reads)
+	   */
+	  if (mouse.dx < -2048)
+	      mouse.dx = -2048;
+	  if (mouse.dx >  2048)
+	      mouse.dx =  2048;
+
+	  if (mouse.dy < -2048)
+	      mouse.dy = -2048;
+	  if (mouse.dy >  2048)
+	      mouse.dy =  2048;
 	}
 	MSE_INT_ON();
 }
 
-static void release_mouse(struct inode * inode, struct file * file)
+/*
+ * close access to the mouse (can deal with multiple
+ * opens if allowed in the future)
+ */
+
+static void close_mouse(struct inode * inode, struct file * file)
 {
-	MSE_INT_OFF();
-	mouse.active = 0;
-	mouse.ready = 0;
-	free_irq(mouse_irq);
+	if (--mouse.active == 0) {
+	    MSE_INT_OFF();
+	    free_irq(mouse_irq);
+	}
 }
 
+/*
+ * open access to the mouse, currently only one open is
+ * allowed.
+ */
+
 static int open_mouse(struct inode * inode, struct file * file)
 {
 	if (!mouse.present)
 		return -EINVAL;
 	if (mouse.active)
 		return -EBUSY;
-	mouse.active = 1;
 	mouse.ready = 0;
 	mouse.dx = 0;
 	mouse.dy = 0;
 	mouse.buttons = 0x87;
-	if (request_irq(mouse_irq, mouse_interrupt)) {
-		mouse.active = 0;
+	if (request_irq(mouse_irq, mouse_interrupt))
 		return -EBUSY;
-	}
+	mouse.active = 1;
 	MSE_INT_ON();
 	return 0;
 }
 
+/*
+ * writes are disallowed
+ */
 
 static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
 {
 	return -EINVAL;
 }
 
+/*
+ * read mouse data.  Currently never blocks.
+ */
+
 static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
 {
-	int i;
+	int r;
+	int dx;
+	int dy;
+	unsigned char buttons; 
 
 	if (count < 3)
 		return -EINVAL;
+	if ((r = verify_area(VERIFY_WRITE, buffer, count)))
+		return r;
 	if (!mouse.ready)
 		return -EAGAIN;
+
+	/*
+	 * Obtain the current mouse parameters and limit as appropriate for
+	 * the return data format.  Interrupts are only disabled while 
+	 * obtaining the parameters, NOT during the puts_fs_byte() calls,
+	 * so paging in put_fs_byte() does not effect mouse tracking.
+	 */
+
 	MSE_INT_OFF();
-	put_fs_byte(mouse.buttons | 0x80, buffer);
-	if (mouse.dx < -127)
-		mouse.dx = -127;
-	if (mouse.dx > 127)
-		mouse.dx =  127;
-	put_fs_byte((char)mouse.dx, buffer + 1);
-	if (mouse.dy < -127)
-		mouse.dy = -127;
-	if (mouse.dy > 127)
-		mouse.dy =  127;
-	put_fs_byte((char) -mouse.dy, buffer + 2);
-	for (i = 3; i < count; i++)
-		put_fs_byte(0x00, buffer + i);
-	mouse.dx = 0;
-	mouse.dy = 0;
+	dx = mouse.dx;
+	dy = mouse.dy;
+	if (dx < -127)
+	    dx = -127;
+	if (dx > 127)
+	    dx = 127;
+	if (dy < -127)
+	    dy = -127;
+	if (dy > 127)
+	    dy = 127;
+	buttons = mouse.buttons;
+	mouse.dx -= dx;
+	mouse.dy -= dy;
 	mouse.ready = 0;
 	MSE_INT_ON();
-	return i;
+
+	put_fs_byte(buttons | 0x80, buffer);
+	put_fs_byte((char)dx, buffer + 1);
+	put_fs_byte((char)dy, buffer + 2);
+	for (r = 3; r < count; r++)
+	    put_fs_byte(0x00, buffer + r);
+	return r;
 }
 
+/*
+ * select for mouse input, must disable the mouse interrupt while checking
+ * mouse.ready/select_wait() to avoid race condition (though in reality
+ * such a condition is not fatal to the proper operation of the mouse since
+ * multiple interrupts generally occur).
+ */
+
 static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
 {
-	if (sel_type != SEL_IN)
-		return 0;
-	if (mouse.ready)
-		return 1;
-	select_wait(&mouse.wait, wait);
-	return 0;
+    int r = 0;
+
+    if (sel_type == SEL_IN) {
+    	MSE_INT_OFF();
+    	if (mouse.ready) {
+    	    r = 1;
+    	} else {
+	    select_wait(&mouse.wait, wait);
+    	}
+    	MSE_INT_ON();
+    }
+    return(r);
 }
 
 struct file_operations bus_mouse_fops = {
@@ -147,7 +211,7 @@
 	NULL, 		/* mouse_ioctl */
 	NULL,		/* mouse_mmap */
 	open_mouse,
-	release_mouse,
+	close_mouse,
 };
 
 unsigned long bus_mouse_init(unsigned long kmem_start)
diff --git a/drivers/char/console.c b/drivers/char/console.c
index cfe6b66..3e2b0da 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -62,7 +62,7 @@
 static void clear_selection(void);
 
 /* Variables for selection control. */
-#define SEL_BUFFER_SIZE 2048
+#define SEL_BUFFER_SIZE TTY_BUF_SIZE
 static int sel_cons;
 static int sel_start = -1;
 static int sel_end;
@@ -73,7 +73,6 @@
 
 extern void vt_init(void);
 extern void register_console(void (*proc)(const char *));
-extern void compute_shiftstate(void);
 
 unsigned long	video_num_columns;		/* Number of text columns	*/
 unsigned long	video_num_lines;		/* Number of test lines		*/
@@ -225,9 +224,9 @@
 	"\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
 	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
 	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
-	"\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
+	"\376\245\376\376\376\376\231\376\235\376\376\376\232\376\376\341"
 	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
-	"\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
+	"\376\244\225\242\223\376\224\366\233\227\243\226\201\376\376\230",
 /* vt100 graphics */
 (unsigned char *)
 	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -244,9 +243,9 @@
 	"\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
 	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
 	"\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
-/* IBM graphics: minimal translations (CR, LF, LL, SO, SI and ESC) */
+/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
 (unsigned char *)
-	"\000\001\002\003\004\005\006\007\010\011\000\013\000\000\000\000"
+	"\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
 	"\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
 	"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
 	"\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
@@ -311,16 +310,17 @@
 
 static inline void __set_origin(unsigned short offset)
 {
+	unsigned long flags;
 #ifdef CONFIG_SELECTION
 	clear_selection();
 #endif /* CONFIG_SELECTION */
-	cli();
+	save_flags(flags); cli();
 	__origin = offset;
 	outb_p(12, video_port_reg);
 	outb_p(offset >> 8, video_port_val);
 	outb_p(13, video_port_reg);
 	outb_p(offset, video_port_val);
-	sti();
+	restore_flags(flags);
 }
 
 void scrollback(int lines)
@@ -365,11 +365,13 @@
 
 static inline void set_cursor(int currcons)
 {
+	unsigned long flags;
+
 	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
 		return;
 	if (__real_origin != __origin)
 		set_origin(__real_origin);
-	cli();
+	save_flags(flags); cli();
 	if (deccm) {
 		outb_p(14, video_port_reg);
 		outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
@@ -377,7 +379,7 @@
 		outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
 	} else
 		hide_cursor(currcons);
-	sti();
+	restore_flags(flags);
 }
 
 static void scrup(int currcons, unsigned int t, unsigned int b)
@@ -597,7 +599,8 @@
 		video_erase_char = (color << 8) | ' ';
 }
 
-static void default_attr(int currcons) {
+static void default_attr(int currcons)
+{
 	intensity = 1;
 	underline = 0;
 	reverse = 0;
@@ -1436,7 +1439,7 @@
 	gotoxy(currcons,orig_x,orig_y);
 	update_screen(fg_console);
 	printable = 1;
-	printk("Console: %s %s %dx%d, %d virtual consoles\n",
+	printk("Console: %s %s %ldx%ld, %d virtual consoles\n",
 		can_do_color?"colour":"mono",
 		display_desc,
 		video_num_columns,video_num_lines,
@@ -1512,7 +1515,6 @@
 	set_origin(fg_console);
 	set_cursor(new_console);
 	set_leds();
-	compute_shiftstate();
 	lock = 0;
 }
 
@@ -1553,7 +1555,7 @@
 
 #ifdef CONFIG_SELECTION
 /* correction factor for when screen is hardware-scrolled */
-#define	hwscroll_offset ((__real_origin - __origin) << 1)
+#define	hwscroll_offset (currcons == fg_console ? ((__real_origin - __origin) << 1) : 0)
 
 /* set reverse video on characters s-e of console with selection. */
 static void highlight(const int currcons, const int s, const int e)
diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c
index 0596fab..3193e1f 100644
--- a/drivers/char/defkeymap.c
+++ b/drivers/char/defkeymap.c
@@ -20,7 +20,7 @@
 	0x010b,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x001c,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x001b,	0x0021,	0x0040,	0x0023,	0x0024,	0x0025,	0x005e,	
@@ -37,41 +37,41 @@
 	0x010b,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x020b,	0x0601,	0x0602,	0x0117,	0x0600,	0x020a,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
-	0x0200,	0x001b,	0x0031,	0x0040,	0x0033,	0x0024,	0x0035,	0x0036,	
-	0x007b,	0x005b,	0x005d,	0x007d,	0x005c,	0x003d,	0x007f,	0x0009,	
-	0x0071,	0x0077,	0x0065,	0x0072,	0x0074,	0x0079,	0x0075,	0x0069,	
-	0x006f,	0x0070,	0x005b,	0x007e,	0x0201,	0x0702,	0x0061,	0x0073,	
-	0x0064,	0x0066,	0x0067,	0x0068,	0x006a,	0x006b,	0x006c,	0x003b,	
-	0x0027,	0x0060,	0x0700,	0x005c,	0x007a,	0x0078,	0x0063,	0x0076,	
-	0x0062,	0x006e,	0x006d,	0x002c,	0x002e,	0x002f,	0x0700,	0x030c,	
-	0x0703,	0x0020,	0x0207,	0x050c,	0x050d,	0x050e,	0x050f,	0x0510,	
+	0x0200,	0x0200,	0x0200,	0x0040,	0x0200,	0x0024,	0x0200,	0x0200,	
+	0x007b,	0x005b,	0x005d,	0x007d,	0x005c,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x007e,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0703,	0x0200,	0x0207,	0x050c,	0x050d,	0x050e,	0x050f,	0x0510,	
 	0x0511,	0x0512,	0x0513,	0x0514,	0x0515,	0x0208,	0x0202,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
 	0x0302,	0x0303,	0x0300,	0x0310,	0x0206,	0x0200,	0x007c,	0x0516,	
 	0x0517,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x030e,	0x0702,	0x030d,	0x001c,	0x0701,	0x0205,	0x0114,	0x0603,	
-	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
-	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	}, {
-	0x0200,	0x001b,	0x0021,	0x0040,	0x0023,	0x0024,	0x0025,	0x005e,	
-	0x0026,	0x002a,	0x0028,	0x0029,	0x005f,	0x002b,	0x007f,	0x0009,	
-	0x0051,	0x0057,	0x0045,	0x0052,	0x0054,	0x0059,	0x0055,	0x0049,	
-	0x004f,	0x0050,	0x007b,	0x007d,	0x0201,	0x0702,	0x0041,	0x0053,	
-	0x0044,	0x0046,	0x0047,	0x0048,	0x004a,	0x004b,	0x004c,	0x003a,	
-	0x0022,	0x007e,	0x0700,	0x007c,	0x005a,	0x0058,	0x0043,	0x0056,	
-	0x0042,	0x004e,	0x004d,	0x003c,	0x003e,	0x003f,	0x0700,	0x030c,	
-	0x0703,	0x0020,	0x0207,	0x010a,	0x010b,	0x010c,	0x010d,	0x010e,	
-	0x010f,	0x0110,	0x0111,	0x0112,	0x0113,	0x0208,	0x0203,	0x0307,	
-	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
-	0x0302,	0x0303,	0x0300,	0x0310,	0x0206,	0x0200,	0x003e,	0x010a,	
-	0x010b,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	}, {
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
+	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
+	0x0302,	0x0303,	0x0300,	0x0310,	0x0206,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
+	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0000,	0x001b,	0x001c,	0x001d,	0x001e,	
@@ -80,7 +80,7 @@
 	0x000f,	0x0010,	0x001b,	0x001d,	0x0201,	0x0702,	0x0001,	0x0013,	
 	0x0004,	0x0006,	0x0007,	0x0008,	0x000a,	0x000b,	0x000c,	0x0200,	
 	0x0007,	0x0000,	0x0700,	0x001c,	0x001a,	0x0018,	0x0003,	0x0016,	
-	0x0002,	0x000e,	0x000d,	0x0200,	0x020e,	0x007f,	0x0700,	0x030c,	
+	0x0002,	0x000e,	0x000d,	0x0200,	0x0200,	0x007f,	0x0700,	0x030c,	
 	0x0703,	0x0000,	0x0207,	0x0100,	0x0101,	0x0102,	0x0103,	0x0104,	
 	0x0105,	0x0106,	0x0107,	0x0108,	0x0109,	0x0208,	0x0204,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -88,16 +88,16 @@
 	0x010b,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x001c,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
-	0x0200,	0x0200,	0x0200,	0x0000,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0200,	0x0200,	0x0200,	0x0200,	0x001f,	0x0200,	0x0200,	0x0200,	
-	0x0011,	0x0017,	0x0005,	0x0012,	0x0014,	0x0019,	0x0015,	0x0009,	
-	0x000f,	0x0010,	0x0200,	0x0200,	0x0201,	0x0702,	0x0001,	0x0013,	
-	0x0004,	0x0006,	0x0007,	0x0008,	0x000a,	0x000b,	0x000c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x001a,	0x0018,	0x0003,	0x0016,	
-	0x0002,	0x000e,	0x000d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -105,16 +105,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0011,	0x0017,	0x0005,	0x0012,	0x0014,	0x0019,	0x0015,	0x0009,	
-	0x000f,	0x0010,	0x0200,	0x0200,	0x0201,	0x0702,	0x0001,	0x0013,	
-	0x0004,	0x0006,	0x0007,	0x0008,	0x000a,	0x000b,	0x000c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x001a,	0x0018,	0x0003,	0x0016,	
-	0x0002,	0x000e,	0x000d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -122,16 +122,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x020c,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0011,	0x0017,	0x0005,	0x0012,	0x0014,	0x0019,	0x0015,	0x0009,	
-	0x000f,	0x0010,	0x0200,	0x0200,	0x0201,	0x0702,	0x0001,	0x0013,	
-	0x0004,	0x0006,	0x0007,	0x0008,	0x000a,	0x000b,	0x000c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x001a,	0x0018,	0x0003,	0x0016,	
-	0x0002,	0x000e,	0x000d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -139,7 +139,7 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x081b,	0x0831,	0x0832,	0x0833,	0x0834,	0x0835,	0x0836,	
@@ -156,16 +156,16 @@
 	0x050b,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x001c,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0851,	0x0857,	0x0845,	0x0852,	0x0854,	0x0859,	0x0855,	0x0849,	
-	0x084f,	0x0850,	0x0200,	0x0200,	0x0201,	0x0702,	0x0841,	0x0853,	
-	0x0844,	0x0846,	0x0847,	0x0848,	0x084a,	0x084b,	0x084c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x085a,	0x0858,	0x0843,	0x0856,	
-	0x0842,	0x084e,	0x084d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -173,16 +173,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0871,	0x0877,	0x0865,	0x0872,	0x0874,	0x0879,	0x0875,	0x0869,	
-	0x086f,	0x0870,	0x0200,	0x0200,	0x0201,	0x0702,	0x0861,	0x0873,	
-	0x0864,	0x0866,	0x0867,	0x0868,	0x086a,	0x086b,	0x086c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x087a,	0x0878,	0x0863,	0x0876,	
-	0x0862,	0x086e,	0x086d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -190,16 +190,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0851,	0x0857,	0x0845,	0x0852,	0x0854,	0x0859,	0x0855,	0x0849,	
-	0x084f,	0x0850,	0x0200,	0x0200,	0x0201,	0x0702,	0x0841,	0x0853,	
-	0x0844,	0x0846,	0x0847,	0x0848,	0x084a,	0x084b,	0x084c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x085a,	0x0858,	0x0843,	0x0856,	
-	0x0842,	0x084e,	0x084d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -207,16 +207,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0811,	0x0817,	0x0805,	0x0812,	0x0814,	0x0819,	0x0815,	0x0809,	
-	0x080f,	0x0810,	0x0200,	0x0200,	0x0201,	0x0702,	0x0801,	0x0813,	
-	0x0804,	0x0806,	0x0807,	0x0808,	0x080a,	0x080b,	0x080c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x081a,	0x0818,	0x0803,	0x0816,	
-	0x0802,	0x080e,	0x080d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -224,16 +224,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x020c,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0811,	0x0817,	0x0805,	0x0812,	0x0814,	0x0819,	0x0815,	0x0809,	
-	0x080f,	0x0810,	0x0200,	0x0200,	0x0201,	0x0702,	0x0801,	0x0813,	
-	0x0804,	0x0806,	0x0807,	0x0808,	0x080a,	0x080b,	0x080c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x081a,	0x0818,	0x0803,	0x0816,	
-	0x0802,	0x080e,	0x080d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -241,16 +241,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0811,	0x0817,	0x0805,	0x0812,	0x0814,	0x0819,	0x0815,	0x0809,	
-	0x080f,	0x0810,	0x0200,	0x0200,	0x0201,	0x0702,	0x0801,	0x0813,	
-	0x0804,	0x0806,	0x0807,	0x0808,	0x080a,	0x080b,	0x080c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x081a,	0x0818,	0x0803,	0x0816,	
-	0x0802,	0x080e,	0x080d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -258,16 +258,16 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, {
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
-	0x0811,	0x0817,	0x0805,	0x0812,	0x0814,	0x0819,	0x0815,	0x0809,	
-	0x080f,	0x0810,	0x0200,	0x0200,	0x0201,	0x0702,	0x0801,	0x0813,	
-	0x0804,	0x0806,	0x0807,	0x0808,	0x080a,	0x080b,	0x080c,	0x0200,	
-	0x0200,	0x0200,	0x0700,	0x0200,	0x081a,	0x0818,	0x0803,	0x0816,	
-	0x0802,	0x080e,	0x080d,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0201,	0x0702,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0700,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0700,	0x030c,	
 	0x0703,	0x0200,	0x0207,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0208,	0x0200,	0x0307,	
 	0x0308,	0x0309,	0x030b,	0x0304,	0x0305,	0x0306,	0x030a,	0x0301,	
@@ -275,7 +275,7 @@
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x030e,	0x0702,	0x030d,	0x0200,	0x0701,	0x0205,	0x0114,	0x0603,	
 	0x0118,	0x0601,	0x0602,	0x0117,	0x0600,	0x0119,	0x0115,	0x0116,	
-	0x0122,	0x010c,	0x010d,	0x0120,	0x0121,	0x0110,	0x0311,	0x0200,	
+	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	0x0200,	
 	}, 
 };
@@ -313,9 +313,6 @@
 	0, 
 	0, 
 	0, 
-	0, 
-	0, 
-	'\033', '[', 'M', 0, 
 };
 
 char *func_table[NR_FUNC] = {
@@ -351,7 +348,4 @@
 	func_buf + 148,
 	func_buf + 149,
 	func_buf + 150,
-	func_buf + 151,
-	func_buf + 152,
-	func_buf + 153,
 };
diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map
index bb4c368..cf43e69 100644
--- a/drivers/char/defkeymap.map
+++ b/drivers/char/defkeymap.map
@@ -5,7 +5,6 @@
 	alt     keycode   2 = Meta_one        
 keycode   3 = two              at               at              
 	control keycode   3 = nul             
-	shift control keycode 3 = nul
 	alt     keycode   3 = Meta_two        
 keycode   4 = three            numbersign      
 	control keycode   4 = Escape          
@@ -31,7 +30,6 @@
 	alt     keycode  11 = Meta_zero       
 keycode  12 = minus            underscore       backslash       
 	control keycode  12 = Control_underscore
-	shift control keycode 12 = Control_underscore
 	alt     keycode  12 = Meta_minus      
 keycode  13 = equal            plus            
 	alt     keycode  13 = Meta_equal      
@@ -56,7 +54,7 @@
 	control keycode  27 = Control_bracketright
 	alt     keycode  27 = Meta_bracketright
 keycode  28 = Return          
-	alt     keycode  28 = Meta_Control_m
+	alt     keycode  28 = 0x080d          
 keycode  29 = Control         
 keycode  30 = a               
 keycode  31 = s               
@@ -89,7 +87,6 @@
 keycode  51 = comma            less            
 	alt     keycode  51 = Meta_comma      
 keycode  52 = period           greater         
-	control keycode  52 = Dead_Key_Next
 	alt     keycode  52 = Meta_period     
 keycode  53 = slash            question        
 	control keycode  53 = Delete          
@@ -199,13 +196,13 @@
 keycode 111 = Remove          
 	altgr   control keycode 111 = Boot            
 	control alt     keycode 111 = Boot            
-keycode 112 = Macro
-keycode 113 = F13
-keycode 114 = F14
-keycode 115 = Help
-keycode 116 = Do
-keycode 117 = F17
-keycode 118 = KP_MinPlus
+keycode 112 =
+keycode 113 =
+keycode 114 =
+keycode 115 =
+keycode 116 =
+keycode 117 =
+keycode 118 =
 keycode 119 =
 keycode 120 =
 keycode 121 =
@@ -247,6 +244,3 @@
 string F24 = ""
 string F25 = ""
 string F26 = ""
-string Macro = "\033[M"
-string Help = ""
-string Do = ""
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 7fcc884..0b1ae66 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -7,8 +7,8 @@
  * the assembly version by Linus (with diacriticals added)
  *
  * Some additional features added by Christoph Niemann (ChN), March 1993
+ *
  * Loadable keymaps by Risto Kankkunen, May 1993
- * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
  */
 
 #define KEYBOARD_IRQ 1
@@ -25,8 +25,6 @@
 
 #include <asm/bitops.h>
 
-#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
 #ifndef KBD_DEFFLAGS
 
 #ifdef CONFIG_KBD_META
@@ -66,11 +64,8 @@
 
 unsigned char kbd_read_mask = 0x01;	/* modified by psaux.c */
 
-/*
- * global state includes the following, and various static variables
- * in this module: prev_scancode, shift_state, diacr, npadch,
- *   dead_key_next, last_console
- */
+unsigned long kbd_dead_keys = 0;
+unsigned long kbd_prev_dead_keys = 0;
 
 /* shift state counters.. */
 static unsigned char k_down[NR_SHIFT] = {0, };
@@ -79,10 +74,6 @@
 
 static int want_console = -1;
 static int last_console = 0;		/* last used VC */
-static int dead_key_next = 0;
-static int shift_state = 0;
-static int npadch = -1;		        /* -1 or number assembled on pad */
-static unsigned char diacr = 0;
 static char rep = 0;			/* flag telling character repeat */
 struct kbd_struct kbd_table[NR_CONSOLES];
 static struct kbd_struct * kbd = kbd_table;
@@ -112,17 +103,25 @@
 
 /* maximum values each key_handler can handle */
 const int max_vals[] = {
-	255, NR_FUNC - 1, 14, 17, 4, 255, 3, NR_SHIFT,
+	255, NR_FUNC - 1, 13, 16, 4, 255, 3, NR_SHIFT,
 	255, 9, 3
 };
 
-const int NR_TYPES = SIZE(max_vals);
+const int NR_TYPES = (sizeof(max_vals) / sizeof(int));
+
+#define E0_BASE 96
+
+static int shift_state = 0;
+static int diacr = -1;
+static int npadch = 0;
 
 static void put_queue(int);
-static unsigned char handle_diacr(unsigned char);
+static unsigned int handle_diacr(unsigned int);
 
 static struct pt_regs * pt_regs;
 
+static inline void translate(unsigned char scancode);
+
 static inline void kb_wait(void)
 {
 	int i;
@@ -132,65 +131,22 @@
 			break;
 }
 
-/*
- * Translation of escaped scancodes to keysyms.
- * This should be user-settable.
- */
-#define E0_BASE 96
-
-#define E0_KPENTER (E0_BASE+0)
-#define E0_RCTRL   (E0_BASE+1)
-#define E0_KPSLASH (E0_BASE+2)
-#define E0_PRSCR   (E0_BASE+3)
-#define E0_RALT    (E0_BASE+4)
-#define E0_BREAK   (E0_BASE+5)  /* (control-pause) */
-#define E0_HOME    (E0_BASE+6)
-#define E0_UP      (E0_BASE+7)
-#define E0_PGUP    (E0_BASE+8)
-#define E0_LEFT    (E0_BASE+9)
-#define E0_RIGHT   (E0_BASE+10)
-#define E0_END     (E0_BASE+11)
-#define E0_DOWN    (E0_BASE+12)
-#define E0_PGDN    (E0_BASE+13)
-#define E0_INS     (E0_BASE+14)
-#define E0_DEL     (E0_BASE+15)
-/* BTC */
-#define E0_MACRO   (E0_BASE+16)
-/* LK450 */
-#define E0_F13     (E0_BASE+17)
-#define E0_F14     (E0_BASE+18)
-#define E0_HELP    (E0_BASE+19)
-#define E0_DO      (E0_BASE+20)
-#define E0_F17     (E0_BASE+21)
-#define E0_KPMINPLUS (E0_BASE+22)
-
-static unsigned char e0_keys[128] = {
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x00-0x07 */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x08-0x0f */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x10-0x17 */
-  0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,	      /* 0x18-0x1f */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x20-0x27 */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x28-0x2f */
-  0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,	      /* 0x30-0x37 */
-  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,	      /* 0x38-0x3f */
-  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,	      /* 0x40-0x47 */
-  E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, E0_KPMINPLUS, E0_END,  /* 0x48-0x4f */
-  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,	      /* 0x50-0x57 */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x58-0x5f */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x60-0x67 */
-  0, 0, 0, 0, 0, 0, 0, E0_MACRO,		      /* 0x68-0x6f */
-  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x70-0x77 */
-  0, 0, 0, 0, 0, 0, 0, 0			      /* 0x78-0x7f */
-};
+static inline void send_cmd(unsigned char c)
+{
+	kb_wait();
+	outb(c,0x64);
+}
 
 static void keyboard_interrupt(int int_pt_regs)
 {
 	unsigned char scancode;
-	static unsigned char prev_scancode = 0; /* remember E0, E1 */
-	char up_flag;
-	char raw_mode;
 
 	pt_regs = (struct pt_regs *) int_pt_regs;
+	kbd_prev_dead_keys |= kbd_dead_keys;
+	if (!kbd_dead_keys)
+		kbd_prev_dead_keys = 0;
+	kbd_dead_keys = 0;
+	send_cmd(0xAD);		/* disable keyboard */
 	kb_wait();
 	if ((inb_p(0x64) & kbd_read_mask) != 0x01)
 		goto end_kbd_intr;
@@ -205,85 +161,109 @@
 	}
 	tty = TTY_TABLE(0);
 	kbd = kbd_table + fg_console;
-	if ((raw_mode = vc_kbd_flag(kbd,VC_RAW))) {
+	if (vc_kbd_flag(kbd,VC_RAW)) {
+		memset(k_down, 0, sizeof(k_down));
+		memset(key_down, 0, sizeof(key_down));
+		shift_state = 0;
 		put_queue(scancode);
-		/* we do not return yet, because we want to maintain
-		   the key_down array, so that we have the correct
-		   values when finishing RAW mode or when changing VT's */
-	}
-	if (scancode == 0xe0 || scancode == 0xe1) {
-		prev_scancode = scancode;
 		goto end_kbd_intr;
-	}
+	} else
+		translate(scancode);
+end_kbd_intr:
+	send_cmd(0xAE);		/* enable keyboard */
+	return;
+}
 
+static inline void translate(unsigned char scancode)
+{
+	char break_flag;
+     	static unsigned char e0_keys[] = {
+		0x1c,	/* keypad enter */
+		0x1d,	/* right control */
+		0x35,	/* keypad slash */
+		0x37,	/* print screen */
+		0x38,	/* right alt */
+		0x46,	/* break (control-pause) */
+		0x47,	/* editpad home */
+		0x48,	/* editpad up */
+		0x49,	/* editpad pgup */
+		0x4b,	/* editpad left */
+		0x4d,	/* editpad right */
+		0x4f,	/* editpad end */
+		0x50,	/* editpad dn */
+		0x51,	/* editpad pgdn */
+		0x52,	/* editpad ins */
+		0x53,	/* editpad del */
+#ifdef LK450
+		0x3d,	/* f13 */
+		0x3e,	/* f14 */
+		0x3f,	/* help */
+		0x40,	/* do */
+		0x41,	/* f17 */
+		0x4e	/* keypad minus/plus */
+#endif
+#ifdef BTC
+		0x6f    /* macro */
+#endif
+	};
+
+	if (scancode == 0xe0) {
+		set_kbd_dead(KGD_E0);
+		return;
+	}
+	if (scancode == 0xe1) {
+		set_kbd_dead(KGD_E1);
+		return;
+	}
 	/*
-	 *  Convert scancode to keysym, using prev_scancode.
+	 *  The keyboard maintains its own internal caps lock and num lock
+	 *  statuses. In caps lock mode E0 AA precedes make code and E0 2A
+	 *  follows break code. In num lock mode, E0 2A precedes make
+	 *  code and E0 AA follows break code. We do our own book-keeping,
+	 *  so we will just ignore these.
 	 */
-	up_flag = scancode > 0x7f;
+	if (kbd_dead(KGD_E0) && (scancode == 0x2a || scancode == 0xaa ||
+				 scancode == 0x36 || scancode == 0xb6))
+		return;
+
+	/* map two byte scancodes into one byte id's */
+
+	break_flag = scancode > 0x7f;
 	scancode &= 0x7f;
 
-	if (prev_scancode == 0xe0) {
-	  prev_scancode = 0;
-  	  /*
-	   *  The keyboard maintains its own internal caps lock and num lock
-	   *  statuses. In caps lock mode E0 AA precedes make code and E0 2A
-	   *  follows break code. In num lock mode, E0 2A precedes make
-	   *  code and E0 AA follows break code. We do our own book-keeping,
-	   *  so we will just ignore these.
-	   */
-	  /*
-	   *  For my keyboard there is no caps lock mode, but there are
-	   *  both Shift-L and Shift-R modes. The former mode generates
-	   *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
-	   *  So, we should also ignore the latter. - aeb@cwi.nl
-	   */
-	  if (scancode == 0x2a || scancode == 0x36)
-	    goto end_kbd_intr;
-
-	  if (e0_keys[scancode])
-	    scancode = e0_keys[scancode];
-	  else if (!raw_mode) {
-	    printk("keyboard: unknown scancode e0 %02x\n", scancode);
-	    goto end_kbd_intr;
-	  }
-	} else if (scancode >= E0_BASE && !raw_mode) {
-	  printk("keyboard: scancode (%02x) not in range 00 - %2x\n",
-		 scancode, E0_BASE - 1);
-	  goto end_kbd_intr;
+	if (kbd_dead(KGD_E0)) {
+		int i;
+		for (i = 0; i < sizeof(e0_keys); i++)
+			if (scancode == e0_keys[i]) {
+				scancode = E0_BASE + i;
+				i = -1;
+				break;
+			}
+		if (i != -1) {
+#if 0
+			printk("keyboard: unknown scancode e0 %02x\n", scancode);
+#endif
+			return;
+		}
+	} else if (scancode >= E0_BASE) {
+#if 0
+		printk("keyboard: scancode (%02x) not in range 00 - %2x\n", scancode, E0_BASE - 1);
+#endif
+		return;
 	}
 
-	/*
-	 * At this point the variable `scancode' contains the keysym.
-	 * We keep track of the up/down status of the key, and
-	 * return the keysym if in MEDIUMRAW mode.
-	 * Note that the present code requires the application to keep
-	 * track of the up/down status as well - it would probably be
-	 * better to   put_queue(scancode + (up_flag ? 0200 : 0))  .
-	 */
-
-	if (up_flag) {
+	rep = 0;
+	if (break_flag)
 		clear_bit(scancode, key_down);
-		rep = 0;
-	} else
+	else
 		rep = set_bit(scancode, key_down);
 
-	if (raw_mode)
-	        goto end_kbd_intr;
-
 	if (vc_kbd_flag(kbd, VC_MEDIUMRAW)) {
 		put_queue(scancode);
-		goto end_kbd_intr;
+		return;
 	}
 
 	/*
-	 * Small change in philosophy: earlier we defined repetition by
-	 *	 rep = scancode == prev_keysym;
-	 *	 prev_keysym = scancode;
-	 * but now by the fact that the depressed key was down already.
-	 * Does this ever make a difference?
-	 */
-
-	/*
 	 *  Repeat a key only if the input buffers are empty or the
 	 *  characters get echoed locally. This makes key repeat usable
 	 *  with slow applications and under heavy loads.
@@ -295,10 +275,8 @@
 		u_short key_code;
 
 		key_code = key_map[shift_state][scancode];
-		(*key_handler[key_code >> 8])(key_code & 0xff, up_flag);
+		(*key_handler[key_code >> 8])(key_code & 0xff, break_flag);
 	}
-
-end_kbd_intr:
 }
 
 static void put_queue(int ch)
@@ -372,13 +350,13 @@
 	if (!pt_regs)
 		return;
 	printk("\n");
-	printk("EIP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip);
+	printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
 	if (pt_regs->cs & 3)
-		printk(" ESP: %04x:%08x",0xffff & pt_regs->ss,pt_regs->esp);
-	printk(" EFLAGS: %08x\n",pt_regs->eflags);
-	printk("EAX: %08x EBX: %08x ECX: %08x EDX: %08x\n",
+		printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
+	printk(" EFLAGS: %08lx\n",pt_regs->eflags);
+	printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
 		pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
-	printk("ESI: %08x EDI: %08x EBP: %08x",
+	printk("ESI: %08lx EDI: %08lx EBP: %08lx",
 		pt_regs->esi, pt_regs->edi, pt_regs->ebp);
 	printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
 		0xffff & pt_regs->ds,0xffff & pt_regs->es,
@@ -393,7 +371,7 @@
 		/* pressing srcoll lock 2nd time sends ^Q, ChN */
 		put_queue(START_CHAR(tty));
 	else
-		/* pressing scroll lock 1st time sends ^S, ChN */
+		/* pressing srcoll lock 1st time sends ^S, ChN */
 		put_queue(STOP_CHAR(tty));
 	chg_vc_kbd_flag(kbd,VC_SCROLLOCK);
 }
@@ -442,10 +420,6 @@
 	ctrl_alt_del();
 }
 
-static void dead_next(void)
-{
-        dead_key_next = 1;
-}
 
 static void do_spec(unsigned char value, char up_flag)
 {
@@ -454,12 +428,12 @@
 		NULL,		enter,		show_ptregs,	show_mem,
 		show_state,	send_intr,	lastcons, 	caps_toggle,
 		num,		hold,		scrll_forw,	scrll_back,
-		boot_it,	caps_on,        dead_next
+		boot_it,	caps_on
 	};
 
-	if (up_flag)
+	if (value >= sizeof(fn_table)/sizeof(fnp))
 		return;
-	if (value >= SIZE(fn_table))
+	if (up_flag)
 		return;
 	if (!fn_table[value])
 		return;
@@ -471,14 +445,7 @@
 	if (up_flag)
 		return;		/* no action, if this is a key release */
 
-        if (diacr)
-                value = handle_diacr(value);
-
-        if (dead_key_next) {
-                dead_key_next = 0;
-                diacr = value;
-                return;
-        }
+	value = handle_diacr(value);
 
 	/* kludge... but works for ISO 8859-1 */
 	if (vc_kbd_flag(kbd,VC_CAPSLOCK))
@@ -490,13 +457,8 @@
 	put_queue(value);
 }
 
-#define A_GRAVE  '`'
-#define A_ACUTE  '\''
-#define A_CFLEX  '^'
-#define A_TILDE  '~'
-#define A_DIAER  '"'
 static unsigned char ret_diacr[] =
-        {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
+	{'`', '\'', '^', '~', '"' };		/* Must not end with 0 */
 
 /* If a dead key pressed twice, output a character corresponding to it,	*/
 /* otherwise just remember the dead key.				*/
@@ -506,12 +468,11 @@
 	if (up_flag)
 		return;
 
-        value = ret_diacr[value];
-        if (diacr == value) {   /* pressed twice */
-                diacr = 0;
-                put_queue(value);
-                return;
-        }
+	if (diacr == value) {	/* pressed twice */
+		diacr = -1;
+		put_queue(ret_diacr[value]);
+		return;
+	}
 	diacr = value;
 }
 
@@ -519,59 +480,40 @@
 /* if space if pressed, return a character corresponding the pending	*/
 /* dead key, otherwise try to combine the two.				*/
 
-unsigned char handle_diacr(unsigned char ch)
+unsigned int handle_diacr(unsigned int ch)
 {
-        static struct {
-          unsigned char diacr, base, result;
-        } accent_table[] = {
-          {A_GRAVE, 'A', '\300'},       {A_GRAVE, 'a', '\340'},
-          {A_ACUTE, 'A', '\301'},       {A_ACUTE, 'a', '\341'},
-          {A_CFLEX, 'A', '\302'},       {A_CFLEX, 'a', '\342'},
-          {A_TILDE, 'A', '\303'},       {A_TILDE, 'a', '\343'},
-          {A_DIAER, 'A', '\304'},       {A_DIAER, 'a', '\344'},
-          {'O',     'A', '\305'},       {'o',     'a', '\345'},
-          {'0',     'A', '\305'},       {'0',     'a', '\345'},
-          {'A',     'A', '\305'},       {'a',     'a', '\345'},
-          {'A',     'E', '\306'},       {'a',     'e', '\346'},
-          {',',     'C', '\307'},       {',',     'c', '\347'},
-          {A_GRAVE, 'E', '\310'},       {A_GRAVE, 'e', '\350'},
-          {A_ACUTE, 'E', '\311'},       {A_ACUTE, 'e', '\351'},
-          {A_CFLEX, 'E', '\312'},       {A_CFLEX, 'e', '\352'},
-          {A_DIAER, 'E', '\313'},       {A_DIAER, 'e', '\353'},
-          {A_GRAVE, 'I', '\314'},       {A_GRAVE, 'i', '\354'},
-          {A_ACUTE, 'I', '\315'},       {A_ACUTE, 'i', '\355'},
-          {A_CFLEX, 'I', '\316'},       {A_CFLEX, 'i', '\356'},
-          {A_DIAER, 'I', '\317'},       {A_DIAER, 'i', '\357'},
-          {'-',     'D', '\320'},       {'-',     'd', '\360'}, /* eth */
-          {A_TILDE, 'N', '\321'},       {A_TILDE, 'n', '\361'},
-          {A_GRAVE, 'O', '\322'},       {A_GRAVE, 'o', '\362'},
-          {A_ACUTE, 'O', '\323'},       {A_ACUTE, 'o', '\363'},
-          {A_CFLEX, 'O', '\324'},       {A_CFLEX, 'o', '\364'},
-          {A_TILDE, 'O', '\325'},       {A_TILDE, 'o', '\365'},
-          {A_DIAER, 'O', '\326'},       {A_DIAER, 'o', '\366'},
-          {'/',     'O', '\330'},       {'/',     'o', '\370'},
-          {A_GRAVE, 'U', '\331'},       {A_GRAVE, 'u', '\371'},
-          {A_ACUTE, 'U', '\332'},       {A_ACUTE, 'u', '\372'},
-          {A_CFLEX, 'U', '\333'},       {A_CFLEX, 'u', '\373'},
-          {A_DIAER, 'U', '\334'},       {A_DIAER, 'u', '\374'},
-          {A_ACUTE, 'Y', '\335'},       {A_ACUTE, 'y', '\375'},
-          {'T',     'H', '\336'},       {'t',     'h', '\376'}, /* thorn */
-          {'s',     's', '\337'},       {A_DIAER, 'y', '\377'},
-          {'s',     'z', '\337'},       {'i',     'j', '\377'}
-        };
-        int d = diacr;
-        int i;
+	static unsigned char accent_table[5][64] = {
+	" \300BCD\310FGH\314JKLMN\322PQRST\331VWXYZ[\\]^_"
+	"`\340bcd\350fgh\354jklmn\362pqrst\371vwxyz{|}~",   /* accent grave */
 
-        diacr = 0;
-        if (ch == ' ')
-                return d;
+	" \301BCD\311FGH\315JKLMN\323PQRST\332VWX\335Z[\\]^_"
+	"`\341bcd\351fgh\355jklmn\363pqrst\372vwx\375z{|}~", /* accent acute */
 
-        for (i = 0; i < SIZE(accent_table); i++)
-          if(accent_table[i].diacr == d && accent_table[i].base == ch)
-            return accent_table[i].result;
+	" \302BCD\312FGH\316JKLMN\324PQRST\333VWXYZ[\\]^_"
+	"`\342bcd\352fgh\356jklmn\364pqrst\373vwxyz{|}~",   /* circumflex */
 
-        put_queue(d);
-        return ch;
+	" \303BCDEFGHIJKLM\321\325PQRSTUVWXYZ[\\]^_"
+	"`\343bcdefghijklm\361\365pqrstuvwxyz{|}~",	    /* tilde */
+
+	" \304BCD\313FGH\317JKLMN\326PQRST\334VWXYZ[\\]^_"
+	"`\344bcd\353fgh\357jklmn\366pqrst\374vwx\377z{|}~" /* dieresis */
+	};
+	int d = diacr, e;
+
+	if (diacr == -1)
+		return ch;
+
+	diacr = -1;
+	if (ch == ' ')
+		return ret_diacr[d];
+
+	if (ch >= 64 && ch <= 122) {
+		e = accent_table[d][ch - 64];
+		if (e != ch)
+			return e;
+	}
+	put_queue(ret_diacr[d]);
+	return ch;
 }
 
 static void do_cons(unsigned char value, char up_flag)
@@ -585,16 +527,13 @@
 {
 	if (up_flag)
 		return;
-	if (value < SIZE(func_table))
-	        puts_queue(func_table[value]);
-	else
-	        printk("do_fn called with value=%d\n", value);
+	puts_queue(func_table[value]);
 }
 
 static void do_pad(unsigned char value, char up_flag)
 {
-	static char *pad_chars = "0123456789+-*/\015,.?";
-	static char *app_map = "pqrstuvwxylSRQMnn?";
+	static char *pad_chars = "0123456789+-*/\015,.";
+	static char *app_map = "pqrstuvwxylSRQMnn";
 
 	if (up_flag)
 		return;		/* no action, if this is a key release */
@@ -671,8 +610,6 @@
 	}
 
 	if (up_flag) {
-	        /* handle the case that two shift or control
-		   keys are depressed simultaneously */
 		if (k_down[value])
 			k_down[value]--;
 	} else
@@ -684,37 +621,12 @@
 		shift_state &= ~ (1 << value);
 
 	/* kludge */
-	if (up_flag && shift_state != old_state && npadch != -1) {
+	if (up_flag && shift_state != old_state && npadch != 0) {
 		put_queue(npadch);
-		npadch = -1;
+		npadch = 0;
 	}
 }
 
-/* called after returning from RAW mode or when changing consoles -
-   recompute k_down[] and shift_state from key_down[] */
-void compute_shiftstate(void)
-{
-        int i, j, k, sym, val;
-
-        shift_state = 0;
-	for(i=0; i < SIZE(k_down); i++)
-	  k_down[i] = 0;
-
-	for(i=0; i < SIZE(key_down); i++)
-	  if(key_down[i]) {	/* skip this word if not a single bit on */
-	    k = (i<<5);
-	    for(j=0; j<32; j++,k++)
-	      if(test_bit(k, key_down)) {
-		sym = key_map[0][k];
-		if(KTYP(sym) == KT_SHIFT) {
-		  val = KVAL(sym);
-		  k_down[val]++;
-		  shift_state |= (1<<val);
-	        }
-	      }
-	  }
-}
-
 static void do_meta(unsigned char value, char up_flag)
 {
 	if (up_flag)
@@ -732,10 +644,7 @@
 	if (up_flag)
 		return;
 
-	if (npadch == -1)
-	        npadch = value;
-	else
-	        npadch = (npadch * 10 + value) % 1000;
+	npadch = (npadch * 10 + value) % 1000;
 }
 
 /* done stupidly to avoid coding in any dependencies of
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index ce0d63d..ec40297 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -8,6 +8,7 @@
 
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/sched.h>
 #include <linux/lp.h>
 #include <linux/malloc.h>
@@ -432,8 +433,8 @@
 	unsigned int testvalue = 0;
 	int count = 0;
 
-	if (register_chrdev(6,"lp",&lp_fops)) {
-		printk("unable to get major 6 for line printer\n");
+	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
+		printk("unable to get major %d for line printer\n", LP_MAJOR);
 		return kmem_start;
 	}
 	/* take on all known port values */
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 7dbcaa2..f30d39f 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -9,6 +9,7 @@
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/tty.h>
 #include <linux/mouse.h>
 #include <linux/tpqic02.h>
@@ -110,6 +111,20 @@
 	return 0;
 }
 
+static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
+{
+	int read1, read2;
+
+	read1 = read_mem(inode, file, buf, count);
+	if (read1 < 0)
+		return read1;
+	read2 = vread(buf + read1, (char *) file->f_pos, count - read1);
+	if (read2 < 0)
+		return read2;
+	file->f_pos += read2;
+	return read1 + read2;
+}
+
 static int read_port(struct inode * inode,struct file * file,char * buf, int count)
 {
 	unsigned int i = file->f_pos;
@@ -224,7 +239,6 @@
 	return file->f_pos;
 }
 
-#define read_kmem	read_mem
 #define write_kmem	write_mem
 #define mmap_kmem	mmap_mem
 #define zero_lseek	null_lseek
@@ -351,13 +365,13 @@
 
 long chr_dev_init(long mem_start, long mem_end)
 {
-	if (register_chrdev(1,"mem",&memory_fops))
-		printk("unable to get major 1 for memory devs\n");
+	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
+		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 	mem_start = tty_init(mem_start);
 #ifdef CONFIG_PRINTER
 	mem_start = lp_init(mem_start);
 #endif
-#if defined (CONFIG_BUSMOUSE) || defined (CONFIG_QUICKPORT_MOUSE) || \
+#if defined (CONFIG_BUSMOUSE) || defined (CONFIG_82C710_MOUSE) || \
     defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
     defined (CONFIG_ATIXL_BUSMOUSE)
 	mem_start = mouse_init(mem_start);
diff --git a/drivers/char/mouse.c b/drivers/char/mouse.c
index 3ea9c47..debc6ea 100644
--- a/drivers/char/mouse.c
+++ b/drivers/char/mouse.c
@@ -19,6 +19,7 @@
 #include <linux/mouse.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 
 /*
  * note that you can remove any or all of the drivers by undefining
@@ -91,7 +92,8 @@
 #ifdef CONFIG_ATIXL_BUSMOUSE
  	kmem_start = atixl_busmouse_init(kmem_start);
 #endif
-	if (register_chrdev(10,"mouse",&mouse_fops))
-		printk("unable to get major 10 for mouse devices\n");
+	if (register_chrdev(MOUSE_MAJOR,"mouse",&mouse_fops))
+		printk("unable to get major %d for mouse devices\n",
+		       MOUSE_MAJOR);
 	return kmem_start;
 }
diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c
index 0fd252d..8a5bef4 100644
--- a/drivers/char/msbusmouse.c
+++ b/drivers/char/msbusmouse.c
@@ -62,7 +62,7 @@
 	outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
 	outb((inb(MS_MSE_DATA_PORT) & 0xdf), MS_MSE_DATA_PORT);
 
-	if (dx != 0 || dy != 0 || buttons != mouse.buttons) {
+	if (dx != 0 || dy != 0 || buttons != mouse.buttons || ((~buttons) & 0x07)) {
 		mouse.buttons = buttons;
 		mouse.dx += dx;
 		mouse.dy += dy;
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index e313633..487b16d 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -14,6 +14,11 @@
  *
  * Modified by Johan Myreen (jem@cs.hut.fi) 04Aug93
  *   to include support for QuickPort mouse.
+ *
+ * Changed references to "QuickPort" with "82C710" since "QuickPort"
+ * is not what this driver is all about -- QuickPort is just a
+ * connector type, and this driver is for the mouse port on the Chips
+ * & Technologies 82C710 interface chip. 15Nov93 jem@cs.hut.fi
  */
 
 /* Uncomment the following line if your mouse needs initialization. */
@@ -67,7 +72,7 @@
 #define AUX_IRQ		12
 #define AUX_BUF_SIZE	2048
 
-/* QuickPort definitions */
+/* 82C710 definitions */
 
 #define QP_DATA         0x310		/* Data Port I/O Address */
 #define QP_STATUS       0x311		/* Status Port I/O Address */
@@ -99,7 +104,7 @@
 static int aux_present = 0;
 static int poll_aux_status(void);
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 static int qp_present = 0;
 static int qp_busy = 0;
 static int qp_data = QP_DATA;
@@ -203,11 +208,11 @@
 }
 
 /*
- * Interrupt handler for the QuickPort. A character
+ * Interrupt handler for the 82C710 mouse port. A character
  * is waiting in the 82C710.
  */
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 static void qp_interrupt(int cpl)
 {
 	int head = queue->head;
@@ -236,17 +241,17 @@
 	aux_busy = 0;
 }
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 static void release_qp(struct inode * inode, struct file * file)
 {
 	unsigned char status;
 
 	if (!poll_qp_status())
-	        printk("Warning: QuickPort device busy in release_qp()\n");
+	        printk("Warning: Mouse device busy in release_qp()\n");
 	status = inb_p(qp_status);
 	outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
 	if (!poll_qp_status())
-	        printk("Warning: QuickPort device busy in release_qp()\n");
+	        printk("Warning: Mouse device busy in release_qp()\n");
 	free_irq(QP_IRQ);
 	qp_busy = 0;
 }
@@ -280,7 +285,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 /*
  * Install interrupt handler.
  * Enable the device, enable interrupts. Set qp_busy
@@ -313,7 +318,7 @@
 	outb_p(status, qp_status);              /* Enable interrupts */
 
 	while (!poll_qp_status()) {
-	        printk("Error: QuickPort device busy in open_qp()\n");
+	        printk("Error: Mouse device busy in open_qp()\n");
 		return -EBUSY;
         }
 
@@ -344,9 +349,9 @@
 }
 
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 /*
- * Write to the QuickPort device.
+ * Write to the 82C710 mouse device.
  */
 
 static int write_qp(struct inode * inode, struct file * file, char * buffer, int count)
@@ -428,19 +433,19 @@
 
 
 /*
- * Initialize driver. First check for QuickPort device; if found
- * forget about the Aux port and use the QuickPort functions.
+ * Initialize driver. First check for a 82C710 chip; if found
+ * forget about the Aux port and use the *_qp functions.
  */
 
 unsigned long psaux_init(unsigned long kmem_start)
 {
         int qp_found = 0;
 
-#ifdef CONFIG_QUICKPORT_MOUSE
-	printk("Probing QuickPort device.\n");
-        if (qp_found = probe_qp()) {
-	        printk("QuickPort pointing device detected -- driver installed.\n");
-/*		printk("QuickPort address = %x (should be 0x310)\n", qp_data); */
+#ifdef CONFIG_82C710_MOUSE
+	printk("Probing 82C710 mouse port device.\n");
+        if ((qp_found = probe_qp())) {
+	        printk("82C710 type pointing device detected -- driver installed.\n");
+/*		printk("82C710 address = %x (should be 0x310)\n", qp_data); */
 		qp_present = 1;
 		psaux_fops.write = write_qp;
 		psaux_fops.open = open_qp;
@@ -492,7 +497,7 @@
 	return !(retries==MAX_RETRIES);
 }
 
-#ifdef CONFIG_QUICKPORT_MOUSE
+#ifdef CONFIG_82C710_MOUSE
 /*
  * Wait for device to send output char and flush any input char.
  */
@@ -526,7 +531,7 @@
 }
 
 /*
- * See if we can find a QuickPort device. Read mouse address.
+ * See if we can find a 82C710 device. Read mouse address.
  */
 
 static int probe_qp(void)
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 4cbecab..f3ee83b 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -74,7 +74,7 @@
 
 /*
  * This routine gets called when tty_write has put something into
- * the write_queue. It copies the input to the output-queue of it's
+ * the write_queue. It copies the input to the output-queue of its
  * slave.
  */
 static void pty_write(struct tty_struct * tty)
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index 9ad20f9..d2b6286 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -41,7 +41,7 @@
  *		Enables automatic IRQ detection.  I've put in some
  * 		fixes to this which should make this work much more
  * 		cleanly than it used to in 0.98pl2-6.  It should be
- * 		much less vulnerable to false IRQ's now.
+ * 		much less vulnerable to false IRQs now.
  * 
  * CONFIG_AST_FOURPORT
  *		Enables support for the AST Fourport serial port.
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index 5283f42..45e6958 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -154,25 +154,26 @@
 
 #define REALLY_SLOW_IO		/* it sure is ... */
 
-#include <asm/dma.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
+#include <linux/major.h>
 #include <linux/errno.h>
 #include <linux/mtio.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
-
 #include <linux/tpqic02.h>
 
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
 /* check existence of required configuration parameters */
-#if !defined(TAPE_QIC02_MAJOR) || !defined(TAPE_QIC02_PORT) || \
-    !defined(TAPE_QIC02_IRQ) || !defined(TAPE_QIC02_DMA)
+#if !defined(TAPE_QIC02_PORT) || \
+    !defined(TAPE_QIC02_IRQ) || \
+    !defined(TAPE_QIC02_DMA)
 #error tape_qic02 configuration error
 #endif
 
@@ -220,7 +221,7 @@
 static volatile unsigned dma_mode = 0;		/* !=0 also means DMA in use */
 static 		flag need_rewind = YES;
 
-static dev_t current_tape_dev = (TAPE_QIC02_MAJOR)<<8;
+static dev_t current_tape_dev = QIC02_TAPE_MAJOR << 8;
 static int extra_blocks_left = BLOCKS_BEYOND_EW;
 
 
@@ -560,7 +561,9 @@
 	ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0;
 
 	outb_p(ctlbits & ~QIC_CTL_RESET, QIC_CTL_PORT);	/* de-assert reset */
-	status_dead = ((inb_p(QIC_STAT_PORT) & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL);
+	/* KLUDGE FOR G++ BUG */
+	{ int stat = inb_p(QIC_STAT_PORT);
+	  status_dead = ((stat & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL); }
 	/* if successful, inb(STAT) returned RESETVAL */
 	if (status_dead)
 		printk(TPQIC_NAME ": reset failed!\n");
@@ -2341,7 +2344,7 @@
 
 	/* check iocmd first */
 
-	if (dev_maj != TAPE_QIC02_MAJOR) {
+	if (dev_maj != QIC02_TAPE_MAJOR) {
 		printk(TPQIC_NAME ": Oops! Wrong device?\n");
 		/* A panic() would be appropriate here */
 		return -ENODEV;
@@ -2582,8 +2585,9 @@
 #endif
 
 	/* If we got this far, install driver functions */
-	if (register_chrdev(TAPE_QIC02_MAJOR, TPQIC_NAME, &tape_qic02_fops)) {
-		printk(TPQIC_NAME ": Unable to get chrdev major %d\n", TAPE_QIC02_MAJOR);
+	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC_NAME, &tape_qic02_fops)) {
+		printk(TPQIC_NAME ": Unable to get chrdev major %d\n",
+		       QIC02_TAPE_MAJOR);
 		return kmem_start;
 	}
 
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 160eb64..9a4478b 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -35,6 +35,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/major.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/fcntl.h>
@@ -996,7 +997,7 @@
 	struct tty_struct * tty;
 
 	dev = file->f_rdev;
-	if (MAJOR(dev) != 4) {
+	if (MAJOR(dev) != TTY_MAJOR) {
 		printk("tty_read: bad pseudo-major nr #%d\n", MAJOR(dev));
 		return -EINVAL;
 	}
@@ -1030,8 +1031,8 @@
 
 	dev = file->f_rdev;
 	is_console = (inode->i_rdev == 0x0400);
-	if (MAJOR(dev) != 4) {
-		printk("tty_write: pseudo-major != 4\n");
+	if (MAJOR(dev) != TTY_MAJOR) {
+		printk("tty_write: pseudo-major != TTY_MAJOR\n");
 		return -EINVAL;
 	}
 	dev = MINOR(dev);
@@ -1292,13 +1293,13 @@
 	minor = MINOR(inode->i_rdev);
 	major = MAJOR(inode->i_rdev);
 	noctty = filp->f_flags & O_NOCTTY;
-	if (major == 5) {
+	if (major == TTYAUX_MAJOR) {
 		if (!minor) {
-			major = 4;
+			major = TTY_MAJOR;
 			minor = current->tty;
 		}
 		noctty = 1;
-	} else if (major == 4) {
+	} else if (major == TTY_MAJOR) {
 		if (!minor) {
 			minor = fg_console + 1;
 			noctty = 1;
@@ -1361,8 +1362,8 @@
 	int dev;
 
 	dev = filp->f_rdev;
-	if (MAJOR(dev) != 4) {
-		printk("tty_release: tty pseudo-major != 4\n");
+	if (MAJOR(dev) != TTY_MAJOR) {
+		printk("tty_release: tty pseudo-major != TTY_MAJOR\n");
 		return;
 	}
 	dev = MINOR(filp->f_rdev);
@@ -1379,8 +1380,8 @@
 	struct tty_struct * tty;
 
 	dev = filp->f_rdev;
-	if (MAJOR(dev) != 4) {
-		printk("tty_select: tty pseudo-major != 4\n");
+	if (MAJOR(dev) != TTY_MAJOR) {
+		printk("tty_select: tty pseudo-major != TTY_MAJOR\n");
 		return 0;
 	}
 	dev = MINOR(filp->f_rdev);
@@ -1646,10 +1647,10 @@
 
 	if (sizeof(struct tty_struct) > PAGE_SIZE)
 		panic("size of tty structure > PAGE_SIZE!");
-	if (register_chrdev(4,"tty",&tty_fops))
-		panic("unable to get major 4 for tty device");
-	if (register_chrdev(5,"tty",&tty_fops))
-		panic("unable to get major 5 for tty device");
+	if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
+		panic("unable to get major %d for tty device", TTY_MAJOR);
+	if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops))
+		panic("unable to get major %d for tty device", TTYAUX_MAJOR);
 	for (i=0 ; i< MAX_TTYS ; i++) {
 		tty_table[i] =  0;
 		tty_termios[i] = 0;
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index df5d35d..a819573 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/tty.h>
 #include <linux/fcntl.h>
 
@@ -419,8 +420,8 @@
 	int termios_dev;
 	int retval;
 
-	if (MAJOR(file->f_rdev) != 4) {
-		printk("tty_ioctl: tty pseudo-major != 4\n");
+	if (MAJOR(file->f_rdev) != TTY_MAJOR) {
+		printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n");
 		return -EINVAL;
 	}
 	dev = MINOR(file->f_rdev);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index deb1662..d1069be 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -33,11 +33,10 @@
  * to the current console is done by the main ioctl code.
  */
 
-struct vt_cons vt_cons[NR_CONSOLES];
+struct vt_struct vt_cons[NR_CONSOLES];
 
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
 
-extern void compute_shiftstate(void);
 extern void change_console(unsigned int new_console);
 extern void complete_change_console(unsigned int new_console);
 extern int vt_waitactive(void);
@@ -213,7 +212,6 @@
 		} else if (arg == K_XLATE) {
 			clr_vc_kbd_flag(kbd, VC_RAW);
 			clr_vc_kbd_flag(kbd, VC_MEDIUMRAW);
-			compute_shiftstate();
 		} else if (arg == K_MEDIUMRAW) {
 			clr_vc_kbd_flag(kbd, VC_RAW);
 			set_vc_kbd_flag(kbd, VC_MEDIUMRAW);
@@ -273,7 +271,7 @@
 
 	case KDGKBSENT:
 	{
-		const struct kbsentry *a = (struct kbsentry *)arg;
+		struct kbsentry *a = (struct kbsentry *)arg;
 		char *p;
 		u_char *q;
 
diff --git a/drivers/char/vt_kern.h b/drivers/char/vt_kern.h
index 6341140..978119a 100644
--- a/drivers/char/vt_kern.h
+++ b/drivers/char/vt_kern.h
@@ -8,7 +8,7 @@
 
 #include <linux/vt.h>
 
-extern struct vt_cons {
+extern struct vt_struct {
 	unsigned char	vc_mode;		/* KD_TEXT, ... */
 	unsigned char	vc_kbdraw;
 	unsigned char	vc_kbde0;
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index e310156..bf86c6f 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -1,4 +1,4 @@
-/* el2.c: A shared-memory NS8390 ethernet driver for linux. */
+/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
 /*
     Written 1992,1993 by Donald Becker.
 
@@ -16,7 +16,7 @@
 */
 
 static char *version =
-    "el2.c:v0.99.13 8/30/93 Donald Becker (becker@super.org)\n";
+    "3c503.c:v0.99.13 8/30/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
new file mode 100644
index 0000000..64c4532
--- /dev/null
+++ b/drivers/net/3c507.c
@@ -0,0 +1,899 @@
+/* 3c507.c: An EtherLink16 device driver for Linux. */
+/*
+	Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the Director,
+	National Security Agency.  This software may only be used and distributed
+	according to the terms of the GNU Public License as modified by SRC,
+	incorported herein by reference.
+
+	The author may be reached as becker@super.org or
+	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+	Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
+	and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
+
+	Things remaining to do:
+	Verify that the tx and rx buffers don't have fencepost errors.
+	Move the theory of operation and memory map documentation.
+	The statistics need to be updated correctly.
+*/
+
+static char *version =
+	"3c507.c:v0.03 10/27/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+
+/*
+  Sources:
+	This driver wouldn't have been written with the availability of the
+	Crynwr driver source code.	It provided a known-working implementation
+	that filled in the gaping holes of the Intel documention.  Three cheers
+	for Russ Nelson.
+
+	Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+	info that the casual reader might think that it documents the i82586.
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <errno.h>
+#include <memory.h>
+
+#include "dev.h"
+#include "iow.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(addr, size) kfree_s(addr,size);
+#else
+#include <linux/malloc.h>
+#endif
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/*
+  			Details of the i82586.
+
+   You'll really need the databook to understand the details of this part,
+   but the outline is that the i82586 has two seperate processing units.
+   Both are started from a list of three configuration tables, of which only
+   the last, the System Control Block (SCB), is used after reset-time.  The SCB
+   has the following fileds:
+		Status word
+		Command word
+		Tx/Command block addr.
+		Rx block addr.
+   The command word accepts the following controls for the Tx and Rx units:
+  */
+
+#define	 CUC_START	 0x0100
+#define	 CUC_RESUME	 0x0200
+#define	 CUC_SUSPEND 0x0300
+#define	 RX_START	 0x0010
+#define	 RX_RESUME	 0x0020
+#define	 RX_SUSPEND	 0x0030
+
+/* The Rx unit uses a list of frame descriptors and a list of data buffer
+   descriptors.  We use full-sized (1518 byte) data buffers, so there is
+   a one-to-one pairing of frame descriptors to buffer descriptors.
+
+   The Tx ("command") unit executes a list of commands that look like:
+		Status word		Written by the 82586 when the command is done.
+		Command word	Command in lower 3 bits, post-command action in upper 3
+		Link word		The address of the next command.
+		Parameters		(as needed).
+
+	Some definitions related to the Command Word are:
+ */
+#define CMD_EOL		0x8000			/* The last command of the list, stop. */
+#define CMD_SUSP	0x4000			/* Suspend after doing cmd. */
+#define CMD_INTR	0x2000			/* Interrupt after doing cmd. */
+
+enum commands {
+	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+	struct enet_statistics stats;
+	int last_restart;
+	ushort rx_head;
+	ushort rx_tail;
+	ushort tx_head;
+	ushort tx_cmd_link;
+	ushort tx_reap;
+};
+
+/*
+  		Details of the EtherLink16 Implementation
+  The 3c507 is a generic shared-memory i82586 implementation.
+  The host can map 16K, 32K, 48K, or 64K of the 64K memory into
+  0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
+  */
+
+/* Offsets from the base I/O address. */
+#define	SA_DATA		0	/* Station address data, or 3Com signature. */
+#define MISC_CTRL	6	/* Switch the SA_DATA banks, and bus config bits. */
+#define RESET_IRQ	10	/* Reset the latched IRQ line. */
+#define SIGNAL_CA	11	/* Frob the 82586 Channel Attention line. */
+#define ROM_CONFIG	13
+#define MEM_CONFIG	14
+#define IRQ_CONFIG	15
+
+/* The ID port is used at boot-time to locate the ethercard. */
+#define ID_PORT		0x100
+
+/* Offsets to registers in the mailbox (SCB). */
+#define iSCB_STATUS	0x8
+#define iSCB_CMD		0xA
+#define iSCB_CBL		0xC	/* Command BLock offset. */
+#define iSCB_RFA		0xE	/* Rx Frame Area offset. */
+
+/*
+  What follows in 'init_words[]' is the "program" that is downloaded to the
+  82586 memory.	 It's mostly tables and command blocks, and starts at the
+  reset address 0xfffff6.  This is designed to be similar to the EtherExpress,
+  thus the unusual location of the SCB at 0x0008.
+
+  Even with the additional "don't care" values, doing it this way takes less
+  program space than initializing the individual tables, and I feel it's much
+  cleaner.
+
+  The databook is particularly useless for the first two structures, I had
+  to use the Crynwr driver as an example.
+
+   The memory setup is as follows:
+   */
+
+#define CONFIG_CMD	0x0018
+#define SET_SA_CMD	0x0024
+#define SA_OFFSET	0x002A
+#define IDLELOOP	0x30
+#define TDR_CMD		0x38
+#define TDR_TIME	0x3C
+#define DUMP_CMD	0x40
+#define DIAG_CMD	0x48
+#define SET_MC_CMD	0x4E
+#define DUMP_DATA	0x56	/* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START	0x0100
+#define NUM_TX_BUFS 	4
+#define TX_BUF_SIZE 	(1518+14+20+16) /* packet+header+TBD */
+
+#define RX_BUF_START	0x2000
+#define RX_BUF_SIZE 	(1518+14+18)	/* packet+header+RBD */
+#define RX_BUF_END		(dev->mem_end - dev->mem_start)
+
+/*
+  That's it: only 86 bytes to set up the beast, including every extra
+  command available.  The 170 byte buffer at DUMP_DATA is shared between the
+  Dump command (called only by the diagnostic program) and the SetMulticastList
+  command. 
+
+  To complete the memory setup you only have to write the station address at
+  SA_OFFSET and create the Tx & Rx buffer lists.
+
+  The Tx command chain and buffer list is setup as follows:
+  A Tx command table, with the data buffer pointing to...
+  A Tx data buffer descriptor.  The packet is in a single buffer, rather than
+     chaining together several smaller buffers.
+  A NoOp command, which initially points to itself,
+  And the packet data.
+
+  A transmit is done by filling in the Tx command table and data buffer,
+  re-writing the NoOp command, and finally changing the offset of the last
+  command to point to the current Tx command.  When the Tx command is finished,
+  it jumps to the NoOp, when it loops until the next Tx command changes the
+  "link offset" in the NoOp.  This way the 82586 never has to go through the
+  slow restart sequence.
+
+  The Rx buffer list is set up in the obvious ring structure.  We have enough
+  memory (and low enough interrupt latency) that we can avoid the complicated
+  Rx buffer linked lists by alway associating a full-size Rx data buffer with
+  each Rx data frame.
+
+  I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+  use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+  */
+
+short init_words[] = {
+	0x0000,					/* Set bus size to 16 bits. */
+	0x0000,0x0000,			/* Set control mailbox (SCB) addr. */
+	0,0,					/* pad to 0x000000. */
+	0x0001,					/* Status word that's cleared when init is done. */
+	0x0008,0,0,				/* SCB offset, (skip, skip) */
+
+	0,0xf000|RX_START|CUC_START,	/* SCB status and cmd. */
+	CONFIG_CMD,				/* Command list pointer, points to Configure. */
+	RX_BUF_START,				/* Rx block list. */
+	0,0,0,0,				/* Error count: CRC, align, buffer, overrun. */
+
+	/* 0x0018: Configure command.  Change to put MAC data with packet. */
+	0, CmdConfigure,		/* Status, command.		*/
+	SET_SA_CMD,				/* Next command is Set Station Addr. */
+	0x0804,					/* "4" bytes of config data, 8 byte FIFO. */
+	0x2e40,					/* Magic values, including MAC data location. */
+	0,						/* Unused pad word. */
+
+	/* 0x0024: Setup station address command. */
+	0, CmdSASetup,
+	SET_MC_CMD,				/* Next command. */
+	0xaa00,0xb000,0x0bad,	/* Station address (to be filled in) */
+
+	/* 0x0030: NOP, looping back to itself.	 Point to first Tx buffer to Tx. */
+	0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+	/* 0x0038: A unused Time-Domain Reflectometer command. */
+	0, CmdTDR, IDLELOOP, 0,
+
+	/* 0x0040: An unused Dump State command. */
+	0, CmdDump, IDLELOOP, DUMP_DATA,
+
+	/* 0x0048: An unused Diagnose command. */
+	0, CmdDiagnose, IDLELOOP,
+
+	/* 0x004E: An empty set-multicast-list command. */
+	0, CmdMulticastList, IDLELOOP, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int el16_probe(struct device *dev);	/* Called from Space.c */
+
+static int	el16_probe1(struct device *dev, short ioaddr);
+static int	el16_open(struct device *dev);
+static int	el16_send_packet(struct sk_buff *skb, struct device *dev);
+static void	el16_interrupt(int reg_ptr);
+static void el16_rx(struct device *dev);
+static int	el16_close(struct device *dev);
+static struct enet_statistics *el16_get_stats(struct device *dev);
+
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+void init_82586_mem(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+   If dev->base_addr == 0, probe all likely locations.
+   If dev->base_addr == 1, always return failure.
+   If dev->base_addr == 2, (detachable devices only) alloate space for the
+   device and return success.
+   */
+int
+el16_probe(struct device *dev)
+{
+	/* Don't probe all settable addresses, 0x[23][0-F]0, just common ones. */
+	int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0};
+	int base_addr = dev->base_addr;
+	ushort lrs_state = 0xff, i;
+
+	if (base_addr > 0x1ff)	/* Check a single specified location. */
+		return el16_probe1(dev, base_addr);
+	else if (base_addr > 0)
+		return ENXIO;		/* Don't probe at all. */
+
+	/* Send the ID sequence to the ID_PORT to enable the board. */
+	outb(0x00, ID_PORT);
+	for(i = 0; i < 255; i++) {
+		outb(lrs_state, ID_PORT);
+		lrs_state <<= 1;
+		if (lrs_state & 0x100)
+			lrs_state ^= 0xe7;
+	}
+	outb(0x00, ID_PORT);
+
+	for (port = &ports[0]; *port; port++) {
+		short ioaddr = *port;
+#if 0
+		/* This is my original code. */
+		if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3'
+			&& inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O'
+			&& el16_probe1(dev, *port) == 0)
+			return 0;
+#else
+	/* This is code from jennings@Montrouge.SMR.slb.com, done so that
+	   the string can be printed out. */
+		char res[5];
+		res[0] = inb(ioaddr); res[1] = inb(ioaddr+1);
+		res[2] = inb(ioaddr+2); res[3] = inb(ioaddr+3);
+		res[4] = 0;
+		if (res[0] == '*' && res[1] == '3'
+			&& res[2] == 'C' && res[3] == 'O'
+			&& el16_probe1(dev, *port) == 0)
+		  return 0;
+#endif
+	}
+
+	return ENODEV;			/* ENODEV would be more accurate. */
+}
+
+int el16_probe1(struct device *dev, short ioaddr)
+{
+	int i, irq, irqval;
+
+	printk("%s: 3c507 at %#x,", dev->name, ioaddr);
+
+	/* We should make a few more checks here, like the first three octets of
+	   the S.A. for the manufactor's code. */ 
+
+	irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+	irqval = request_irq(irq, &el16_interrupt);
+	if (irqval) {
+		printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
+		return EAGAIN;
+	}
+	
+	/* We've committed to using the board, and can start filling in *dev. */
+	snarf_region(ioaddr, 16);
+	dev->base_addr = ioaddr;
+
+	outb(0x01, ioaddr + MISC_CTRL);
+	for (i = 0; i < 6; i++) {
+		dev->dev_addr[i] = inb(ioaddr + i);
+		printk(" %02x", dev->dev_addr[i]);
+	}
+
+	if ((dev->mem_start & 0xf) > 0)
+		net_debug = dev->mem_start & 7;
+
+#ifdef MEM_BASE
+	dev->mem_start = MEM_BASE;
+	dev->mem_end = dev->mem_start + 0x10000;
+#else
+	{
+		int base;
+		int size;
+		char mem_config = inb(ioaddr + MEM_CONFIG);
+		if (mem_config & 0x20) {
+			size = 64*1024;
+			base = 0xf00000 + (mem_config & 0x08 ? 0x080000
+							   : ((mem_config & 3) << 17));
+		} else {
+			size = ((mem_config & 3) + 1) << 14;
+			base = 0x0c0000 + ( (mem_config & 0x18) << 12);
+		}
+		if (size != 0x10000)
+			printk("%s: Warning, this version probably only works with 64K of"
+				   "shared memory.\n", dev->name);
+		dev->mem_start = base;
+		dev->mem_end = base + size;
+	}
+#endif
+
+	dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
+	dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+	printk(", IRQ %d, %sternal xcvr, memory %#x-%#x.\n", dev->irq,
+		   dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
+
+	if (net_debug)
+		printk(version);
+
+	/* Initialize the device structure. */
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	dev->open		= el16_open;
+	dev->stop		= el16_close;
+	dev->hard_start_xmit = el16_send_packet;
+	dev->get_stats	= el16_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++)
+		dev->buffs[i] = NULL;
+
+	dev->hard_header	= eth_header;
+	dev->add_arp	= eth_add_arp;
+	dev->queue_xmit = dev_queue_xmit;
+	dev->rebuild_header = eth_rebuild_header;
+	dev->type_trans = eth_type_trans;
+
+	dev->type		= ARPHRD_ETHER;
+	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;
+	}
+
+	/* 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	= sizeof(unsigned long);
+
+	return 0;
+}
+
+
+
+static int
+el16_open(struct device *dev)
+{
+	irq2dev_map[dev->irq] = dev;
+
+	/* Initialize the 82586 memory and start it. */
+	init_82586_mem(dev);
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+static int
+el16_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	short *shmem = (short*)dev->mem_start;
+
+	if (dev->tbusy) {
+		/* If we get here, some higher level has decided we are broken.
+		   There should really be a "kick me" function call instead. */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return 1;
+		if (net_debug > 1)
+			printk("%s: transmit timed out, %s?  ", dev->name,
+				   shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+				   "network cable problem");
+		/* Try to restart the adaptor. */
+		if (lp->last_restart == lp->stats.tx_packets) {
+			if (net_debug > 1) printk("Resetting board.\n");
+			/* Completely reset the adaptor. */
+			init_82586_mem(dev);
+		} else {
+			/* Issue the channel attention signal and hope it "gets better". */
+			if (net_debug > 1) printk("Kicking board.\n");
+			shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+			outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */
+			lp->last_restart = lp->stats.tx_packets;
+		}
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+	}
+
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself. */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	/* For ethernet, fill in the header.  This should really be done by a
+	   higher level, rather than duplicated for each ethernet adaptor. */
+	if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
+		skb->dev = dev;
+		arp_queue (skb);
+		return 0;
+	}
+	skb->arp=1;
+
+	/* Block a timer-based transmit from overlapping. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0)
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	else {
+		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		unsigned char *buf = (void *)(skb+1);
+
+		/* Disable the 82586's input to the interrupt line. */
+		outb(0x80, ioaddr + MISC_CTRL);
+		hardware_send_packet(dev, buf, length);
+		dev->trans_start = jiffies;
+		/* Enable the 82586 interrupt input. */
+		outb(0x84, ioaddr + MISC_CTRL);
+	}
+
+	if (skb->free)
+		kfree_skb (skb, FREE_WRITE);
+
+	/* You might need to clean up and record Tx statistics here. */
+
+	return 0;
+}
+
+/*	The typical workload of the driver:
+	Handle the network interface interrupts. */
+static void
+el16_interrupt(int reg_ptr)
+{
+	int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct net_local *lp;
+	int ioaddr, status, boguscount = 0;
+	ushort ack_cmd = 0;
+	ushort *shmem;
+	
+	if (dev == NULL) {
+		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+	dev->interrupt = 1;
+	
+	ioaddr = dev->base_addr;
+	lp = (struct net_local *)dev->priv;
+	shmem = ((ushort*)dev->mem_start);
+	
+	status = shmem[iSCB_STATUS>>1];
+	
+    if (net_debug > 4) {
+		printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
+    }
+
+	/* Disable the 82586's input to the interrupt line. */
+	outb(0x80, ioaddr + MISC_CTRL);
+
+	/* Reap the Tx packet buffers. */
+	while (lp->tx_reap != lp->tx_head) {
+	  unsigned short tx_status = shmem[lp->tx_reap>>1];
+
+	  if (tx_status == 0) {
+		if (net_debug > 5)  printk("Couldn't reap %#x.\n", lp->tx_reap);
+		break;
+	  }
+	  if (tx_status & 0x2000) {
+		lp->stats.tx_packets++;
+		lp->stats.collisions += tx_status & 0xf;
+		dev->tbusy = 0;
+		mark_bh(INET_BH);		/* Inform upper layers. */
+	  } else {
+		lp->stats.tx_errors++;
+		if (tx_status & 0x0600)  lp->stats.tx_carrier_errors++;
+		if (tx_status & 0x0100)  lp->stats.tx_fifo_errors++;
+		if (!(tx_status & 0x0040))  lp->stats.tx_heartbeat_errors++;
+		if (tx_status & 0x0020)  lp->stats.tx_aborted_errors++;
+	  }
+	  if (net_debug > 5)
+		  printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+	  lp->tx_reap += TX_BUF_SIZE;
+	  if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
+		lp->tx_reap = TX_BUF_START;
+	  if (++boguscount > 4)
+		break;
+	}
+
+	if (status & 0x4000) { /* Packet received. */
+		if (net_debug > 5)
+			printk("Received packet, rx_head %04x.\n", lp->rx_head);
+		el16_rx(dev);
+	}
+
+	/* Acknowledge the interrupt sources. */
+	ack_cmd = status & 0xf000;
+
+	if ((status & 0x0700) != 0x0200 && dev->start) {
+		if (net_debug)
+			printk("%s: Command unit stopped, status %04x, restarting.\n",
+				   dev->name, status);
+		/* If this ever occurs we should really re-write the idle loop, reset
+		   the Tx list, and do a complete restart of the command unit.
+		   For now we rely on the Tx timeout if the resume doesn't work. */
+		ack_cmd |= CUC_RESUME;
+	}
+
+	if ((status & 0x0070) != 0x0040  &&  dev->start) {
+	  static void init_rx_bufs(struct device *);
+		/* The Rx unit is not ready, it must be hung.  Restart the receiver by
+		   initializing the rx buffers, and issuing an Rx start command. */
+		if (net_debug)
+			printk("%s: Rx unit stopped, status %04x, restarting.\n",
+				   dev->name, status);
+		init_rx_bufs(dev);
+		shmem[iSCB_RFA >> 1] = RX_BUF_START;
+		ack_cmd |= RX_START;
+	}
+
+	shmem[iSCB_CMD>>1] = ack_cmd;
+	outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */
+
+	/* Clear the latched interrupt. */
+	outb(0, ioaddr + RESET_IRQ);
+
+	/* Enable the 82586's interrupt input. */
+	outb(0x84, ioaddr + MISC_CTRL);
+
+	return;
+}
+
+static int
+el16_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	ushort *shmem = (short*)dev->mem_start;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Flush the Tx and disable Rx. */
+	shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+	outb(0, ioaddr + SIGNAL_CA);
+
+	/* Disable the 82586's input to the interrupt line. */
+	outb(0x80, ioaddr + MISC_CTRL);
+
+	/* We always physically use the IRQ line, so we don't do free_irq().
+	   We do remove ourselves from the map. */
+
+	irq2dev_map[dev->irq] = 0;
+
+	/* Update the statistics here. */
+
+	return 0;
+}
+
+/* Get the current statistics.	This may be called with the card open or
+   closed. */
+static struct enet_statistics *
+el16_get_stats(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	/* ToDo: decide if there are any useful statistics from the SCB. */
+
+	return &lp->stats;
+}
+
+/* Initialize the Rx-block list. */
+static void
+init_rx_bufs(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned short *write_ptr;
+
+	int cur_rxbuf = lp->rx_head = RX_BUF_START;
+	
+	/* Initialize each Rx frame + data buffer. */
+	do {	/* While there is room for one more. */
+
+	  write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
+
+		*write_ptr++ = 0x0000; 				/* Status */
+		*write_ptr++ = 0x0000;				/* Command */
+		*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
+		*write_ptr++ = cur_rxbuf + 22;		/* Buffer offset */
+		*write_ptr++ = 0x0000; 				/* Pad for dest addr. */
+		*write_ptr++ = 0x0000;
+		*write_ptr++ = 0x0000;
+		*write_ptr++ = 0x0000; 				/* Pad for source addr. */
+		*write_ptr++ = 0x0000;
+		*write_ptr++ = 0x0000;
+		*write_ptr++ = 0x0000;				/* Pad for protocol. */
+		
+		*write_ptr++ = 0x0000;				/* Buffer: Actual count */
+		*write_ptr++ = -1;					/* Buffer: Next (none). */
+		*write_ptr++ = cur_rxbuf + 0x20;		/* Buffer: Address low */
+		*write_ptr++ = 0x0000;
+		/* Finally, the number of bytes in the buffer. */
+		*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+		
+		lp->rx_tail = cur_rxbuf;
+		cur_rxbuf += RX_BUF_SIZE;
+	} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+	
+	/* Terminate the list by setting the EOL bit, and wrap the pointer to make
+	   the list a ring. */
+	write_ptr = (unsigned short *)
+	  (dev->mem_start + lp->rx_tail + 2);
+	*write_ptr++ = 0xC000;					/* Command, mark as last. */
+	*write_ptr++ = lp->rx_head;				/* Link */
+
+}
+
+void
+init_82586_mem(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	short ioaddr = dev->base_addr;
+	ushort *shmem = (short*)dev->mem_start;
+
+	/* Enable loopback to protect the wire while starting up,
+	   and hold the 586 in reset during the memory initialization. */
+	outb(0x20, ioaddr + MISC_CTRL);
+
+	/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
+#ifdef old
+	memcpy((void*)dev->mem_start+0xfff6, init_words, 10);
+#else
+	memcpy((void*)dev->mem_end-10, init_words, 10);
+#endif
+	/* Write the words at 0x0000. */
+	memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+
+	/* Fill in the station address. */
+	memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+		   sizeof(dev->dev_addr));
+
+	/* The Tx-block list is written as needed.  We just set up the values. */
+	lp->tx_cmd_link = IDLELOOP + 4;
+	lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+	init_rx_bufs(dev);
+
+	/* Start the 586 by releasing the reset line, but leave loopback. */
+	outb(0xA0, ioaddr + MISC_CTRL);
+
+	/* This was time consuming to track down: you need to give two channel
+	   attention signals to reliably start up the i82586. */
+	outb(0, ioaddr + SIGNAL_CA);
+
+	{
+		int boguscnt = 50;
+		while (shmem[iSCB_STATUS>>1] == 0)
+			if (--boguscnt == 0) {
+				printk("%s: i82586 initialization timed out with status %04x,"
+					   "cmd %04x.\n", dev->name,
+					   shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+				break;
+			}
+		/* Issue channel-attn -- the 82586 won't start. */
+		outb(0, ioaddr + SIGNAL_CA);
+	}
+
+	/* Disable loopback and enable interrupts. */
+	outb(0x84, ioaddr + MISC_CTRL);
+	if (net_debug > 4)
+		printk("%s: Initialized 82586, status %04x.\n", dev->name,
+			   shmem[iSCB_STATUS>>1]);
+	return;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	short ioaddr = dev->base_addr;
+	ushort tx_block = lp->tx_head;
+	ushort *write_ptr =	  (ushort *)(dev->mem_start + tx_block);
+
+	/* Set the write pointer to the Tx block, and put out the header. */
+	*write_ptr++ = 0x0000;				/* Tx status */
+	*write_ptr++ = CMD_INTR|CmdTx;		/* Tx command */
+	*write_ptr++ = tx_block+16;			/* Next command is a NoOp. */
+	*write_ptr++ = tx_block+8;			/* Data Buffer offset. */
+
+	/* Output the data buffer descriptor. */
+	*write_ptr++ = length | 0x8000;		/* Byte count parameter. */
+	*write_ptr++ = -1;					/* No next data buffer. */
+	*write_ptr++ = tx_block+22;			/* Buffer follows the NoOp command. */
+	*write_ptr++ = 0x0000;				/* Buffer address high bits (always zero). */
+
+	/* Output the Loop-back NoOp command. */
+	*write_ptr++ = 0x0000;				/* Tx status */
+	*write_ptr++ = CmdNOp;				/* Tx command */
+	*write_ptr++ = tx_block+16;			/* Next is myself. */
+
+	/* Output the packet at the write pointer. */
+	memcpy(write_ptr, buf, length);
+
+	/* Set the old command link pointing to this send packet. */
+	*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+	lp->tx_cmd_link = tx_block + 20;
+
+	/* Set the next free tx region. */
+	lp->tx_head = tx_block + TX_BUF_SIZE;
+	if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
+		lp->tx_head = TX_BUF_START;
+
+    if (net_debug > 4) {
+		printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
+			   dev->name, ioaddr, length, tx_block, lp->tx_head);
+    }
+
+	if (lp->tx_head != lp->tx_reap)
+		dev->tbusy = 0;
+}
+
+static void
+el16_rx(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	short *shmem = (short*)dev->mem_start;
+	ushort rx_head = lp->rx_head;
+	ushort rx_tail = lp->rx_tail;
+	ushort boguscount = 10;
+	short frame_status;
+
+	while ((frame_status = shmem[rx_head>>1]) < 0) {   /* Command complete */
+		ushort *read_frame =  (short *)(dev->mem_start + rx_head);
+		ushort rfd_cmd = read_frame[1];
+		ushort next_rx_frame = read_frame[2];
+		ushort data_buffer_addr = read_frame[3];
+		ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
+		ushort pkt_len = data_frame[0];
+
+		if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
+			|| pkt_len & 0xC000 != 0xC000) {
+			printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
+				   "next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
+				   frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
+				   pkt_len);
+		} else if ((frame_status & 0x2000) == 0) {
+			/* Frame Rxed, but with error. */
+			lp->stats.rx_errors++;
+			if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
+			if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
+			if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
+			if (frame_status & 0x0100) lp->stats.rx_over_errors++;
+			if (frame_status & 0x0080) lp->stats.rx_length_errors++;
+		} else {
+			/* Malloc up new buffer. */
+			int sksize;
+			struct sk_buff *skb;
+
+			pkt_len &= 0x3fff;
+			sksize = sizeof(struct sk_buff) + pkt_len;
+			skb = alloc_skb(sksize, GFP_ATOMIC);
+			if (skb == NULL) {
+				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+				lp->stats.rx_dropped++;
+				break;
+			}
+			skb->mem_len = sksize;
+			skb->mem_addr = skb;
+			skb->len = pkt_len;
+			skb->dev = dev;
+
+			/* 'skb+1' points to the start of sk_buff data area. */
+			memcpy((unsigned char *) (skb + 1), data_frame + 5, pkt_len);
+		
+#ifdef HAVE_NETIF_RX
+			netif_rx(skb);
+#else
+			skb->lock = 0;
+			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
+				kfree_skbmem(skb, sksize);
+				lp->stats.rx_dropped++;
+				break;
+			}
+#endif
+			lp->stats.rx_packets++;
+		}
+
+		/* Clear the status word and set End-of-List on the rx frame. */
+		read_frame[0] = 0;
+		read_frame[1] = 0xC000;
+		/* Clear the end-of-list on the prev. RFD. */
+		*(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+
+		rx_tail = rx_head;
+		rx_head = next_rx_frame;
+		if (--boguscount == 0)
+			break;
+	}
+
+	lp->rx_head = rx_head;
+	lp->rx_tail = rx_tail;
+}
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
+
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 5fb51f3..f0898d2 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -1,19 +1,19 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-    Written 1993 by Donald Becker.
+	Written 1993 by Donald Becker.
 
-    Copyright 1993 United States Government as represented by the
-    Director, National Security Agency.  This software may be used and
-    distributed according to the terms of the GNU Public License,
-    incorporated herein by reference.
-    
-    This driver is for the 3Com EtherLinkIII series.
+	Copyright 1993 United States Government as represented by the
+	Director, National Security Agency.	 This software may be used and
+	distributed according to the terms of the GNU Public License,
+	incorporated herein by reference.
+	
+	This driver is for the 3Com EtherLinkIII series.
 
-    The author may be reached as becker@super.org or
-    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+	The author may be reached as becker@super.org or
+	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
 */
 
-static char *version = "3c509.c:pl14 10/18/93 becker@super.org\n";
+static char *version = "3c509.c:pl13t 11/24/93 becker@super.org\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -45,7 +45,6 @@
 
 #ifndef HAVE_ALLOC_SKB
 #define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skb(buff,size) kfree_s(buff,size)
 #endif
 
 
@@ -63,25 +62,26 @@
 #define EL3_CMD 0x0e
 #define EL3_STATUS 0x0e
 #define ID_PORT 0x100
-#define  EEPROM_READ 0x80
+#define	 EEPROM_READ 0x80
 
 #define EL3WINDOW(win_num) outw(0x0800+(win_num), ioaddr + EL3_CMD)
 
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO		0x00
+#define RX_FIFO		0x00
+#define RX_STATUS 	0x08
+#define TX_STATUS 	0x0B
+#define TX_FREE		0x0C		/* Remaining free bytes in Tx buffer. */
 
-/* Register window 1 offsets, used in normal operation. */
-#define TX_FREE 0x0C
-#define TX_STATUS 0x0B
-#define TX_FIFO 0x00
-#define RX_STATUS 0x08
-#define RX_FIFO 0x00
-
-#define WN4_MEDIA	0x0A
+#define WN4_MEDIA	0x0A		/* Window 4: Various transceiver/media bits. */
+#define  MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */
 
 struct el3_private {
-    struct enet_statistics stats;
+	struct enet_statistics stats;
 };
 
-static int read_eeprom(int index);
+static ushort id_read_eeprom(int index);
+static ushort read_eeprom(short ioaddr, int index);
 static int el3_open(struct device *dev);
 static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
 static void el3_interrupt(int reg_ptr);
@@ -97,143 +97,198 @@
 
 int el3_probe(struct device *dev)
 {
-    short lrs_state = 0xff, i;
-    short ioaddr, irq;
-    short *phys_addr = (short *)dev->dev_addr;
-    static int current_tag = 0;
+	short lrs_state = 0xff, i;
+	ushort ioaddr, irq, if_port;
+	short *phys_addr = (short *)dev->dev_addr;
+	static int current_tag = 0;
 
-    /* Send the ID sequence to the ID_PORT. */
-    outb(0x00, ID_PORT);
-    outb(0x00, ID_PORT);
-    for(i = 0; i < 255; i++) {
-	outb(lrs_state, ID_PORT);
-	lrs_state <<= 1;
-	lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
-    }
+	/* First check for a board on the EISA bus.	 This first check should
+	   really be in init/main.c, along with a MCA check. */
+	if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0) {
+		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+			if (inw(ioaddr) != 0x6d50)
+				continue;
 
-    /* For the first probe, clear all board's tag registers. */
-    if (current_tag == 0)
-	outb(0xd0, ID_PORT);
-    else		/* Otherwise kill off already-found boards. */
-	outb(0xd8, ID_PORT);
+			irq = inw(ioaddr + 8) >> 12;
+			if_port = inw(ioaddr + 6)>>14;
+			for (i = 0; i < 3; i++)
+				phys_addr[i] = htons(read_eeprom(ioaddr, i));
 
-    if (read_eeprom(7) != 0x6d50) {
-	return -ENODEV;
-    }
+			/* Restore the "Manufacturer ID" to the EEPROM read register. */
+			/* The manual says to restore "Product ID" (reg. 3). !???! */
+			read_eeprom(ioaddr, 7);
 
-    /* Read in EEPROM data, which does contention-select.
-       Only the lowest address board will stay "on-line".
-       3Com got the byte order backwards. */
-    for (i = 0; i < 3; i++) {
-	phys_addr[i] = htons(read_eeprom(i));
-    }
+			/* Was the EISA code an add-on hack?  Nahhhhh... */
+			goto found;
+		}
+	}
 
-    {
-	unsigned short iobase = read_eeprom(8);
-	dev->if_port = iobase >> 14;
-	ioaddr = 0x200 + ((iobase & 0x1f) << 4);
-    }
-    irq = read_eeprom(9) >> 12;
+#ifdef CONFIG_MCA
+	if (MCA_bus) {
+		mca_adaptor_select_mode(1);
+		for (i = 0; i < 8; i++)
+			if ((mca_adaptor_id(i) | 1) == 0x627c) {
+				ioaddr = mca_pos_base_addr(i);
+				irq = inw(ioaddr + 8) >> 12;
+				if_port = inw(ioaddr + 6)>>14;
+				for (i = 0; i < 3; i++)
+					phys_addr[i] = htons(read_eeprom(ioaddr, i));
 
-    /* The current Space.c structure makes it difficult to have more
-       than one adaptor initialized.  Send me email if you have a need for
-       multiple adaptors, and we'll work out something.  -becker@super.org */
-    if (dev->base_addr != 0
-	&&  dev->base_addr != (unsigned short)ioaddr) {
-	return -ENODEV;
-    }
+				mca_adaptor_select_mode(0);
+				goto found;
+			}
+		mca_adaptor_select_mode(0);
 
-    /* Set the adaptor tag so that the next card can be found. */
-    outb(0xd0 + ++current_tag, ID_PORT);
+	}
+#endif	  
 
-    /* Activate the adaptor at the EEPROM location. */
-    outb(0xff, ID_PORT);
+	/* Send the ID sequence to the ID_PORT. */
+	outb(0x00, ID_PORT);
+	outb(0x00, ID_PORT);
+	for(i = 0; i < 255; i++) {
+		outb(lrs_state, ID_PORT);
+		lrs_state <<= 1;
+		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+	}
 
-    EL3WINDOW(0);
-    if (inw(ioaddr) != 0x6d50)
-	return -ENODEV;
+	/* For the first probe, clear all board's tag registers. */
+	if (current_tag == 0)
+		outb(0xd0, ID_PORT);
+	else				/* Otherwise kill off already-found boards. */
+		outb(0xd8, ID_PORT);
 
-    dev->base_addr = ioaddr;
-    dev->irq = irq;
-    snarf_region(dev->base_addr, 16);
+	if (id_read_eeprom(7) != 0x6d50) {
+		return -ENODEV;
+	}
 
-    {
-	char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
-	printk("%s: 3c509 at %#3.3x  tag %d, %s port, address ",
-	       dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
-    }
+	/* Read in EEPROM data, which does contention-select.
+	   Only the lowest address board will stay "on-line".
+	   3Com got the byte order backwards. */
+	for (i = 0; i < 3; i++) {
+		phys_addr[i] = htons(id_read_eeprom(i));
+	}
 
-    /* Read in the station address. */
-    for (i = 0; i < 6; i++)
-	printk(" %2.2x", dev->dev_addr[i]);
-    printk(", IRQ %d.\n", dev->irq);
+	{
+		unsigned short iobase = id_read_eeprom(8);
+		if_port = iobase >> 14;
+		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+	}
+	irq = id_read_eeprom(9) >> 12;
 
-    /* Make up a EL3-specific-data structure. */
-    dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
-    memset(dev->priv, 0, sizeof(struct el3_private));
+	/* The current Space.c structure makes it difficult to have more
+	   than one adaptor initialized.  Send me email if you have a need for
+	   multiple adaptors, and we'll work out something.	 -becker@super.org */
+	if (dev->base_addr != 0
+		&&	dev->base_addr != (unsigned short)ioaddr) {
+		return -ENODEV;
+	}
 
-    if (el3_debug > 0)
-	printk(version);
+	/* Set the adaptor tag so that the next card can be found. */
+	outb(0xd0 + ++current_tag, ID_PORT);
 
-    /* The EL3-specific entries in the device structure. */
-    dev->open = &el3_open;
-    dev->hard_start_xmit = &el3_start_xmit;
-    dev->stop = &el3_close;
-    dev->get_stats = &el3_get_stats;
+	/* Activate the adaptor at the EEPROM location. */
+	outb(0xff, ID_PORT);
+
+	EL3WINDOW(0);
+	if (inw(ioaddr) != 0x6d50)
+		return -ENODEV;
+
+ found:
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+	dev->if_port = if_port;
+	snarf_region(dev->base_addr, 16);
+
+	{
+		char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+		printk("%s: 3c509 at %#3.3x	 tag %d, %s port, address ",
+			   dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
+	}
+
+	/* Read in the station address. */
+	for (i = 0; i < 6; i++)
+		printk(" %2.2x", dev->dev_addr[i]);
+	printk(", IRQ %d.\n", dev->irq);
+
+	/* Make up a EL3-specific-data structure. */
+	dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct el3_private));
+
+	if (el3_debug > 0)
+		printk(version);
+
+	/* The EL3-specific entries in the device structure. */
+	dev->open = &el3_open;
+	dev->hard_start_xmit = &el3_start_xmit;
+	dev->stop = &el3_close;
+	dev->get_stats = &el3_get_stats;
 #ifdef HAVE_MULTICAST
-	dev->set_multicast_list = &set_multicast_list;
+		dev->set_multicast_list = &set_multicast_list;
 #endif
 
-    /* Fill in the generic fields of the device structure. */
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-	dev->buffs[i] = NULL;
+	/* Fill in the generic fields of the device structure. */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		dev->buffs[i] = NULL;
 
-    dev->hard_header	= eth_header;
-    dev->add_arp	= eth_add_arp;
-    dev->queue_xmit	= dev_queue_xmit;
-    dev->rebuild_header	= eth_rebuild_header;
-    dev->type_trans	= eth_type_trans;
+	dev->hard_header	= eth_header;
+	dev->add_arp		= eth_add_arp;
+	dev->queue_xmit		= dev_queue_xmit;
+	dev->rebuild_header = eth_rebuild_header;
+	dev->type_trans		= eth_type_trans;
 
-    dev->type		= ARPHRD_ETHER;
-    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->type			= ARPHRD_ETHER;
+	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;
+	}
 
-    /* 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	= sizeof(unsigned long);
+	/* 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		= sizeof(unsigned long);
 
-    return 0;
+	return 0;
 }
 
-
-static int
-read_eeprom(int index)
+/* Read a word from the EEPROM using the regular EEPROM access register.
+   Assume that we are in register window zero.
+ */
+static ushort read_eeprom(short ioaddr, int index)
 {
-    int timer, bit, word = 0;
-    
-    /* Issue read command, and pause for at least 162 us. for it to complete.
-       Assume extra-fast 16Mhz bus. */
-    outb(EEPROM_READ + index, ID_PORT);
+	int timer;
 
-    /* This should really be done by looking at one of the timer channels. */
-    for (timer = 0; timer < 162*4 + 400; timer++)
-	SLOW_DOWN_IO;
+	outw(EEPROM_READ + index, ioaddr + 10);
+	/* Pause for at least 162 us. for the read to take place. */
+	for (timer = 0; timer < 162*4 + 400; timer++)
+		SLOW_DOWN_IO;
+	return inw(ioaddr + 12);
+}
 
-    for (bit = 15; bit >= 0; bit--)
-	word = (word << 1) + (inb(ID_PORT) & 0x01);
+/* Read a word from the EEPROM when in the ISA ID probe state. */
+static ushort id_read_eeprom(int index)
+{
+	int timer, bit, word = 0;
 	
-    if (el3_debug > 3)
-	printk("  3c509 EEPROM word %d %#4.4x.\n", index, word);
+	/* Issue read command, and pause for at least 162 us. for it to complete.
+	   Assume extra-fast 16Mhz bus. */
+	outb(EEPROM_READ + index, ID_PORT);
 
-    return word;
+	/* This should really be done by looking at one of the timer channels. */
+	for (timer = 0; timer < 162*4 + 400; timer++)
+		SLOW_DOWN_IO;
+
+	for (bit = 15; bit >= 0; bit--)
+		word = (word << 1) + (inb(ID_PORT) & 0x01);
+		
+	if (el3_debug > 3)
+		printk("  3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+	return word;
 }
 
 
@@ -241,398 +296,408 @@
 static int
 el3_open(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
-    int i;
+	int ioaddr = dev->base_addr;
+	int i;
 
-    if (request_irq(dev->irq, &el3_interrupt)) {
-	return -EAGAIN;
-    }
+	if (request_irq(dev->irq, &el3_interrupt)) {
+		return -EAGAIN;
+	}
 
-    EL3WINDOW(0);
-    if (el3_debug > 3)
-	printk("%s: Opening, IRQ %d  status@%x %4.4x.\n", dev->name,
-	       dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
+	EL3WINDOW(0);
+	if (el3_debug > 3)
+		printk("%s: Opening, IRQ %d	 status@%x %4.4x.\n", dev->name,
+			   dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
 
-    /* Activate board: this is probably unnecessary. */
-    outw(0x0001, ioaddr + 4);
+	/* Activate board: this is probably unnecessary. */
+	outw(0x0001, ioaddr + 4);
 
-    irq2dev_map[dev->irq] = dev;
+	irq2dev_map[dev->irq] = dev;
 
-    /* Set the IRQ line. */
-    outw((dev->irq << 12) | 0x0f00, ioaddr + 8);
+	/* Set the IRQ line. */
+	outw((dev->irq << 12) | 0x0f00, ioaddr + 8);
 
-    /* Set the station address in window 2 each time opened. */
-    EL3WINDOW(2);
+	/* Set the station address in window 2 each time opened. */
+	EL3WINDOW(2);
 
-    for (i = 0; i < 6; i++)
-	outb(dev->dev_addr[i], ioaddr + i);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], ioaddr + i);
 
-    if (dev->if_port == 3)
-	/* Start the thinnet transceiver. We should really wait 50ms...*/
-	outw(0x1000, ioaddr + EL3_CMD);
-    else if (dev->if_port == 0) {
-	/* 10baseT interface, enabled link beat and jabber check. */
-	EL3WINDOW(4);
-	outw(inw(ioaddr + WN4_MEDIA) | 0x00C0, ioaddr + WN4_MEDIA);
-    }
+	if (dev->if_port == 3)
+		/* Start the thinnet transceiver. We should really wait 50ms...*/
+		outw(0x1000, ioaddr + EL3_CMD);
+	else if (dev->if_port == 0) {
+		/* 10baseT interface, enabled link beat and jabber check. */
+		EL3WINDOW(4);
+		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+	}
 
-    /* Switch to register set 1 for normal use. */
-    EL3WINDOW(1);
+	/* Switch to register set 1 for normal use. */
+	EL3WINDOW(1);
 
-    outw(0x8005, ioaddr + EL3_CMD); /* Accept b-case and phys addr only. */
-    outw(0xA800, ioaddr + EL3_CMD); /* Turn on statistics. */
-    outw(0x2000, ioaddr + EL3_CMD); /* Enable the receiver. */
-    outw(0x4800, ioaddr + EL3_CMD); /* Enable transmitter. */
-    outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
-    dev->interrupt = 0;
-    dev->tbusy = 0;
-    dev->start = 1;
-    outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */
+	outw(0x8005, ioaddr + EL3_CMD); /* Accept b-case and phys addr only. */
+	outw(0xA800, ioaddr + EL3_CMD); /* Turn on statistics. */
+	outw(0x2000, ioaddr + EL3_CMD); /* Enable the receiver. */
+	outw(0x4800, ioaddr + EL3_CMD); /* Enable transmitter. */
+	outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+	outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */
 
-    if (el3_debug > 3)
-	printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
-	       dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+	if (el3_debug > 3)
+		printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
+			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
 
-    return 0;			/* Always succeed */
+	return 0;					/* Always succeed */
 }
 
 static int
 el3_start_xmit(struct sk_buff *skb, struct device *dev)
 {
-    struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+	struct el3_private *lp = (struct el3_private *)dev->priv;
+	int ioaddr = dev->base_addr;
 
-    /* Transmitter timeout, serious problems. */
-    if (dev->tbusy) {
-	int tickssofar = jiffies - dev->trans_start;
-	if (tickssofar < 10)
-	    return 1;
-	printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
-	       dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
-	dev->trans_start = jiffies;
-	/* Issue TX_RESET and TX_START commands. */
-	outw(0x5800, ioaddr + EL3_CMD);	/* TX_RESET */
-	outw(0x4800, ioaddr + EL3_CMD);	/* TX_START */
-	dev->tbusy = 0;
-    }
-
-    if (skb == NULL) {
-	dev_tint(dev);
-	return 0;
-    }
-
-    /* Fill in the ethernet header. */
-    if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
-	skb->dev = dev;
-	arp_queue (skb);
-	return 0;
-    }
-    skb->arp=1;
-
-    if (skb->len <= 0)
-	return 0;
-
-    if (el3_debug > 4) {
-	printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
-	       dev->name, skb->len, inw(ioaddr + EL3_STATUS));
-    }
-
-    if (inw(ioaddr + EL3_STATUS) & 0x0001) { /* IRQ line active, missed one. */
-      printk("%s: Missed interrupt, status %4.4x  Tx %2.2x Rx %4.4x.\n",
-	     dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
-	     inw(ioaddr + RX_STATUS));
-      outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
-      outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
-      outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
-    }
-
-    /* Avoid timer-based retransmission conflicts. */
-    if (set_bit(0, (void*)&dev->tbusy) != 0)
-	printk("%s: Transmitter access conflict.\n", dev->name);
-    else {
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0x00, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	port_write_l(ioaddr + TX_FIFO, (void *)(skb+1), (skb->len + 3) >> 2);
-    
-	dev->trans_start = jiffies;
-	if (inw(ioaddr + TX_FREE) > 1536) {
-	    dev->tbusy=0;
-	} else
-	    /* Interrupt us when the FIFO has room for max-sized packet. */
-	    outw(0x9000 + 1536, ioaddr + EL3_CMD);
-    }
-
-    if (skb->free)
-	kfree_skb (skb, FREE_WRITE);
-
-    /* Clear the Tx status stack. */
-    {
-	short tx_status;
-	int i = 4;
-
-	while (--i > 0  &&  (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
-	    if (el3_debug > 5)
-		printk("        Tx status %4.4x.\n", tx_status);
-	    if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
-	    if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
-	    if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
-	    outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 10)
+			return 1;
+		printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+			   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
+		dev->trans_start = jiffies;
+		/* Issue TX_RESET and TX_START commands. */
+		outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */
+		outw(0x4800, ioaddr + EL3_CMD); /* TX_START */
+		dev->tbusy = 0;
 	}
-    }
-    return 0;
+
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	/* Fill in the ethernet header. */
+	if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
+		skb->dev = dev;
+		arp_queue (skb);
+		return 0;
+	}
+	skb->arp=1;
+
+	if (skb->len <= 0)
+		return 0;
+
+	if (el3_debug > 4) {
+		printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
+			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+	}
+#ifndef final_version
+	{	/* Error-checking code, delete for 1.00. */
+		ushort status = inw(ioaddr + EL3_STATUS);
+		if (status & 0x0001 		/* IRQ line active, missed one. */
+			&& inw(ioaddr + EL3_STATUS) & 1) { 			/* Make sure. */
+			printk("%s: Missed interrupt, status then %04x now %04x"
+				   "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
+				   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+				   inw(ioaddr + RX_STATUS));
+			outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
+			outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
+			outw(0x78ff, ioaddr + EL3_CMD); /* Set all status bits visible. */
+		}
+	}
+#endif
+
+	/* Avoid timer-based retransmission conflicts. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0)
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	else {
+		/* Put out the doubleword header... */
+		outw(skb->len, ioaddr + TX_FIFO);
+		outw(0x00, ioaddr + TX_FIFO);
+		/* ... and the packet rounded to a doubleword. */
+		port_write_l(ioaddr + TX_FIFO, (void *)(skb+1), (skb->len + 3) >> 2);
+	
+		dev->trans_start = jiffies;
+		if (inw(ioaddr + TX_FREE) > 1536) {
+			dev->tbusy=0;
+		} else
+			/* Interrupt us when the FIFO has room for max-sized packet. */
+			outw(0x9000 + 1536, ioaddr + EL3_CMD);
+	}
+
+	if (skb->free)
+		kfree_skb (skb, FREE_WRITE);
+
+	/* Clear the Tx status stack. */
+	{
+		short tx_status;
+		int i = 4;
+
+		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+			if (el3_debug > 5)
+				printk("		Tx status %4.4x.\n", tx_status);
+			if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+			if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
+			if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
+			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+		}
+	}
+	return 0;
 }
 
 /* The EL3 interrupt handler. */
 static void
 el3_interrupt(int reg_ptr)
 {
-    int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
-    struct device *dev = (struct device *)(irq2dev_map[irq]);
-    int ioaddr, status;
-    int i = 0;
+	int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	int ioaddr, status;
+	int i = 0;
 
-    if (dev == NULL) {
-	printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
-	return;
-    }
-
-    if (dev->interrupt)
-	printk("%s: Re-entering the interrupt handler.\n", dev->name);
-    dev->interrupt = 1;
-
-    ioaddr = dev->base_addr;
-    status = inw(ioaddr + EL3_STATUS);
-
-    if (el3_debug > 4)
-	printk("%s: interrupt, status %4.4x.\n", dev->name, status);
-    
-    while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {
-
-	if (status & 0x10)
-	    el3_rx(dev);
-
-	if (status & 0x08) {
-	    if (el3_debug > 5)
-		printk("    TX room bit was handled.\n");
-	    /* There's room in the FIFO for a full-sized packet. */
-	    outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
-	    dev->tbusy = 0;
-	    mark_bh(INET_BH);
+	if (dev == NULL) {
+		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
+		return;
 	}
-	if (status & 0x80)		/* Statistics full. */
-	    update_stats(ioaddr, dev);
+
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	status = inw(ioaddr + EL3_STATUS);
+
+	if (el3_debug > 4)
+		printk("%s: interrupt, status %4.4x.\n", dev->name, status);
 	
-	if (++i > 10) {
-	    printk("%s: Infinite loop in interrupt, status %4.4x.\n",
-		   dev->name, status);
-	    break;
-	}
-	/* Clear the other interrupts we have handled. */
-	outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
-    }
+	while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {
 
-    if (el3_debug > 4) {
-	printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
-	       inw(ioaddr + EL3_STATUS));
-    }
-    
-    dev->interrupt = 0;
-    return;
+		if (status & 0x10)
+			el3_rx(dev);
+
+		if (status & 0x08) {
+			if (el3_debug > 5)
+				printk("	TX room bit was handled.\n");
+			/* There's room in the FIFO for a full-sized packet. */
+			outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
+			dev->tbusy = 0;
+			mark_bh(INET_BH);
+		}
+		if (status & 0x80)				/* Statistics full. */
+			update_stats(ioaddr, dev);
+		
+		if (++i > 10) {
+			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
+				   dev->name, status);
+			break;
+		}
+		/* Clear the other interrupts we have handled. */
+		outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
+	}
+
+	if (el3_debug > 4) {
+		printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
+			   inw(ioaddr + EL3_STATUS));
+	}
+	
+	dev->interrupt = 0;
+	return;
 }
 
 
 static struct enet_statistics *
 el3_get_stats(struct device *dev)
 {
-    struct el3_private *lp = (struct el3_private *)dev->priv;
+	struct el3_private *lp = (struct el3_private *)dev->priv;
 
-    sti();
-    update_stats(dev->base_addr, dev);
-    cli();
-    return &lp->stats;
+	sti();
+	update_stats(dev->base_addr, dev);
+	cli();
+	return &lp->stats;
 }
 
-/* Update statistics.  We change to register window 6, so this
-   should be run single-threaded if the device is active. This
-   is expected to be a rare operation, and not worth a special
-   window-state variable. */
+/*  Update statistics.  We change to register window 6, so this should be run
+	single-threaded if the device is active. This is expected to be a rare
+	operation, and it's simpler for the rest of the driver to assume that
+	window 1 is always valid rather than use a special window-state variable.
+	*/
 static void update_stats(int ioaddr, struct device *dev)
 {
-    struct el3_private *lp = (struct el3_private *)dev->priv;
+	struct el3_private *lp = (struct el3_private *)dev->priv;
 
-    if (el3_debug > 5)
-	printk("   Updating the statistics.\n");
-    /* Turn off statistics updates while reading. */
-    outw(0xB000, ioaddr + EL3_CMD);
-    /* Switch to the stats window, and read everything. */
-    EL3WINDOW(6);
-    lp->stats.tx_carrier_errors	+= inb(ioaddr + 0);
-    lp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
-    /* Multiple collisions. */	   inb(ioaddr + 2);
-    lp->stats.collisions		+= inb(ioaddr + 3);
-    lp->stats.tx_window_errors	+= inb(ioaddr + 4);
-    lp->stats.rx_fifo_errors	+= inb(ioaddr + 5);
-    lp->stats.tx_packets		+= inb(ioaddr + 6);
-    lp->stats.rx_packets		+= inb(ioaddr + 7);
-    /* Tx deferrals */		   inb(ioaddr + 8);
-    inw(ioaddr + 10);	/* Total Rx and Tx octets. */
-    inw(ioaddr + 12);
+	if (el3_debug > 5)
+		printk("   Updating the statistics.\n");
+	/* Turn off statistics updates while reading. */
+	outw(0xB000, ioaddr + EL3_CMD);
+	/* Switch to the stats window, and read everything. */
+	EL3WINDOW(6);
+	lp->stats.tx_carrier_errors 	+= inb(ioaddr + 0);
+	lp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
+	/* Multiple collisions. */	   	inb(ioaddr + 2);
+	lp->stats.collisions			+= inb(ioaddr + 3);
+	lp->stats.tx_window_errors		+= inb(ioaddr + 4);
+	lp->stats.rx_fifo_errors		+= inb(ioaddr + 5);
+	lp->stats.tx_packets			+= inb(ioaddr + 6);
+	lp->stats.rx_packets			+= inb(ioaddr + 7);
+	/* Tx deferrals */				inb(ioaddr + 8);
+	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
+	inw(ioaddr + 12);
 
-    /* Back to window 1, and turn statistics back on. */
-    EL3WINDOW(1);
-    outw(0xA800, ioaddr + EL3_CMD);
-    return;
+	/* Back to window 1, and turn statistics back on. */
+	EL3WINDOW(1);
+	outw(0xA800, ioaddr + EL3_CMD);
+	return;
 }
 
 static int
 el3_rx(struct device *dev)
 {
-    struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
-    short rx_status;
+	struct el3_private *lp = (struct el3_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	short rx_status;
 
-    if (el3_debug > 5)
-	printk("       In rx_packet(), status %4.4x, rx_status %4.4x.\n",
-	       inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
-    while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
-	if (rx_status & 0x4000) { /* Error, update stats. */
-	    short error = rx_status & 0x3C00;
-	    lp->stats.rx_errors++;
-	    switch (error) {
-	    case 0x2000:	lp->stats.rx_over_errors++; break;
-	    case 0x2C00:	lp->stats.rx_length_errors++; break;
-	    case 0x3400:	lp->stats.rx_crc_errors++; break;
-	    case 0x2400:	lp->stats.rx_length_errors++; break;
-	    case 0x3000:	lp->stats.rx_frame_errors++; break;
-	    case 0x0800:	lp->stats.rx_frame_errors++; break;
-	    }
-	}
-	if ( (! (rx_status & 0x4000))
-	    || ! (rx_status & 0x2000)) { /* Dribble bits are OK. */
-	    short pkt_len = rx_status & 0x7ff;
-	    int sksize = sizeof(struct sk_buff) + pkt_len + 3;
-	    struct sk_buff *skb;
+	if (el3_debug > 5)
+		printk("	   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+		if (rx_status & 0x4000) { /* Error, update stats. */
+			short error = rx_status & 0x3C00;
+			lp->stats.rx_errors++;
+			switch (error) {
+			case 0x2000:		lp->stats.rx_over_errors++; break;
+			case 0x2C00:		lp->stats.rx_length_errors++; break;
+			case 0x3400:		lp->stats.rx_crc_errors++; break;
+			case 0x2400:		lp->stats.rx_length_errors++; break;
+			case 0x3000:		lp->stats.rx_frame_errors++; break;
+			case 0x0800:		lp->stats.rx_frame_errors++; break;
+			}
+		}
+		if ( (! (rx_status & 0x4000))
+			|| ! (rx_status & 0x2000)) { /* Dribble bits are OK. */
+			short pkt_len = rx_status & 0x7ff;
+			int sksize = sizeof(struct sk_buff) + pkt_len + 3;
+			struct sk_buff *skb;
 
-	    skb = alloc_skb(sksize, GFP_ATOMIC);
-	    if (el3_debug > 4)
-		printk("       Receiving packet size %d status %4.4x.\n",
-		       pkt_len, rx_status);
-	    if (skb != NULL) {
-		skb->mem_len = sksize;
-		skb->mem_addr = skb;
-		skb->len = pkt_len;
-		skb->dev = dev;
+			skb = alloc_skb(sksize, GFP_ATOMIC);
+			if (el3_debug > 4)
+				printk("	   Receiving packet size %d status %4.4x.\n",
+					   pkt_len, rx_status);
+			if (skb != NULL) {
+				skb->mem_len = sksize;
+				skb->mem_addr = skb;
+				skb->len = pkt_len;
+				skb->dev = dev;
 
-		/* 'skb+1' points to the start of sk_buff data area. */
-		port_read_l(ioaddr+RX_FIFO, (void *)(skb+1),
-			    (pkt_len + 3) >> 2);
+				/* 'skb+1' points to the start of sk_buff data area. */
+				port_read_l(ioaddr+RX_FIFO, (void *)(skb+1),
+							(pkt_len + 3) >> 2);
 
 #ifdef HAVE_NETIF_RX
-		netif_rx(skb);
-		outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
-		continue;
+				netif_rx(skb);
+				outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+				continue;
 #else
-		skb->lock = 0;
-		if (dev_rint((unsigned char *)skb, pkt_len,
-			     IN_SKBUFF,dev)== 0){
-		    if (el3_debug > 6)
-			printk("     dev_rint() happy, status %4.4x.\n",
-			inb(ioaddr + EL3_STATUS));
-		    outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
-		    while (inw(ioaddr + EL3_STATUS) & 0x1000)
-		      printk("  Waiting for 3c509 to discard packet, status %x.\n",
-			     inw(ioaddr + EL3_STATUS) );
-		    if (el3_debug > 6)
-			printk("     discarded packet, status %4.4x.\n",
-			inb(ioaddr + EL3_STATUS));
-		    continue;
-		} else {
-		    printk("%s: receive buffers full.\n", dev->name);
-		    kfree_skbmem(skb, sksize);
-		}
+				skb->lock = 0;
+				if (dev_rint((unsigned char *)skb, pkt_len,
+							 IN_SKBUFF,dev)== 0){
+					if (el3_debug > 6)
+						printk("	 dev_rint() happy, status %4.4x.\n",
+						inb(ioaddr + EL3_STATUS));
+					outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+					while (inw(ioaddr + EL3_STATUS) & 0x1000)
+					  printk("	Waiting for 3c509 to discard packet, status %x.\n",
+							 inw(ioaddr + EL3_STATUS) );
+					if (el3_debug > 6)
+						printk("	 discarded packet, status %4.4x.\n",
+						inb(ioaddr + EL3_STATUS));
+					continue;
+				} else {
+					printk("%s: receive buffers full.\n", dev->name);
+					kfree_s(skb, sksize);
+				}
 #endif
-	    } else if (el3_debug)
-		printk("%s: Couldn't allocate a sk_buff of size %d.\n",
-		       dev->name, sksize);
+			} else if (el3_debug)
+				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+					   dev->name, sksize);
+		}
+		lp->stats.rx_dropped++;
+		outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+		while (inw(ioaddr + EL3_STATUS) & 0x1000)
+		  printk("	Waiting for 3c509 to discard packet, status %x.\n",
+				 inw(ioaddr + EL3_STATUS) );
 	}
-	lp->stats.rx_dropped++;
-	outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
-	while (inw(ioaddr + EL3_STATUS) & 0x1000)
-	  printk("  Waiting for 3c509 to discard packet, status %x.\n",
-		 inw(ioaddr + EL3_STATUS) );
-    }
 
-    if (el3_debug > 5)
-	printk("       Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n",
-	       inw(ioaddr+EL3_STATUS), inw(ioaddr+8));
+	if (el3_debug > 5)
+		printk("	   Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n",
+			   inw(ioaddr+EL3_STATUS), inw(ioaddr+8));
 
-    return 0;
+	return 0;
 }
 
 #ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
-   num_addrs == -1	Promiscuous mode, receive all packets
-   num_addrs == 0	Normal mode, clear multicast list
-   num_addrs > 0	Multicast mode, receive normal and MC packets, and do
-			best-effort filtering.
+   num_addrs == -1		Promiscuous mode, receive all packets
+   num_addrs == 0		Normal mode, clear multicast list
+   num_addrs > 0		Multicast mode, receive normal and MC packets, and do
+						best-effort filtering.
  */
 static void
 set_multicast_list(struct device *dev, int num_addrs, void *addrs)
 {
-    short ioaddr = dev->base_addr;
-    if (num_addrs > 0) {
-    	outw(0x8007, ioaddr + EL3_CMD);
-    } else if (num_addrs < 0) {
-	outw(0x8008, ioaddr + EL3_CMD);
-    } else
-	outw(0x8005, ioaddr + EL3_CMD);
+	short ioaddr = dev->base_addr;
+	if (num_addrs > 0) {
+		outw(0x8007, ioaddr + EL3_CMD);
+	} else if (num_addrs < 0) {
+		outw(0x8008, ioaddr + EL3_CMD);
+	} else
+		outw(0x8005, ioaddr + EL3_CMD);
 }
 #endif
 
 static int
 el3_close(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 
-    if (el3_debug > 2)
-	printk("%s: Shutting down ethercard.\n", dev->name);
+	if (el3_debug > 2)
+		printk("%s: Shutting down ethercard.\n", dev->name);
 
-    dev->tbusy = 1;
-    dev->start = 0;
+	dev->tbusy = 1;
+	dev->start = 0;
 
-    /* Turn off statistics.  We update lp->stats below. */
-    outw(0xB000, ioaddr + EL3_CMD);
+	/* Turn off statistics.	 We update lp->stats below. */
+	outw(0xB000, ioaddr + EL3_CMD);
 
-    /* Disable the receiver and transmitter. */
-    outw(0x1800, ioaddr + EL3_CMD);
-    outw(0x5000, ioaddr + EL3_CMD);
+	/* Disable the receiver and transmitter. */
+	outw(0x1800, ioaddr + EL3_CMD);
+	outw(0x5000, ioaddr + EL3_CMD);
 
-    if (dev->if_port == 3)
-	/* Turn off thinnet power. */
-	outw(0xb800, ioaddr + EL3_CMD);
-    else if (dev->if_port == 0) {
-	/* Disable link beat and jabber, if_port may change ere next open(). */
-	EL3WINDOW(4);
-	outw(inw(ioaddr + WN4_MEDIA) & ~ 0x00C0, ioaddr + WN4_MEDIA);
-    }
+	if (dev->if_port == 3)
+		/* Turn off thinnet power. */
+		outw(0xb800, ioaddr + EL3_CMD);
+	else if (dev->if_port == 0) {
+		/* Disable link beat and jabber, if_port may change ere next open(). */
+		EL3WINDOW(4);
+		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+	}
 
-    free_irq(dev->irq);
-    /* Switching back to window 0 disables the IRQ. */
-    EL3WINDOW(0);
-    /* But we explicitly zero the IRQ line select anyway. */
-    outw(0x0f00, ioaddr + 8);
+	free_irq(dev->irq);
+	/* Switching back to window 0 disables the IRQ. */
+	EL3WINDOW(0);
+	/* But we explicitly zero the IRQ line select anyway. */
+	outw(0x0f00, ioaddr + 8);
 
 
-    irq2dev_map[dev->irq] = 0;
+	irq2dev_map[dev->irq] = 0;
 
-    update_stats(ioaddr, dev);
-    return 0;
+	update_stats(ioaddr, dev);
+	return 0;
 }
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c 3c509.c"
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
  * End:
  */
diff --git a/drivers/net/CONFIG b/drivers/net/CONFIG
index 2ec4be3..c17d07f 100644
--- a/drivers/net/CONFIG
+++ b/drivers/net/CONFIG
@@ -43,5 +43,5 @@
 HP_OPTS		=
 PLIP_OPTS	=
 SLIP_OPTS	= -DSL_DUMP -DSL_COMPRESSED
-DL_OPTS		= # -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
+DL_OPTS		= -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
 AT_OPTS		= # -DLANCE_DMA=5
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index c473451..c535be7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -1,23 +1,13 @@
-# File: net/drv/Makefile
+# File: drivers/net/Makefile
 #
 # Makefile for the Linux network (ethercard) device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
+# This will go away in some future future: hidden configuration files
+# are difficult for users to deal with.
 include CONFIG
 
-.c.s:
-	$(CC) $(CFLAGS) -S -o $*.s $<
-.s.o:
-	$(AS) -o $*.o $<
-.c.o:
-	$(CC) $(CFLAGS) -c -o $*.o $<
-
-NETDRV_OBJS := Space.o auto_irq.o
+NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o)
 CFLAGS := $(CFLAGS) -I../../net/inet
 CPP := $(CPP) -I../../net/inet
 
@@ -27,101 +17,122 @@
 Space.o: Space.c /usr/include/linux/autoconf.h
 	$(CC) $(CFLAGS) $(OPTS) $(DL_OPTS) -c $< -o $@
 
+net_init.o: /usr/include/linux/autoconf.h
+
 ifdef CONFIG_WD80x3
-NETDRV_OBJS := $(NETDRV_OBJS) wd.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(wd.o)
 CONFIG_8390 = CONFIG_8390
 wd.o:	wd.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c wd.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
 endif
 
 ifdef CONFIG_EL2
-NETDRV_OBJS := $(NETDRV_OBJS) 3c503.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c503.o)
 CONFIG_8390 = CONFIG_8390
 3c503.o:	3c503.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c 3c503.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $<
 endif
 
 ifdef CONFIG_NE2000
-NETDRV_OBJS := $(NETDRV_OBJS) ne.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ne.o)
 CONFIG_8390 = CONFIG_8390
 ne.o:	ne.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c ne.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c $<
 endif
 
 ifdef CONFIG_HPLAN
-NETDRV_OBJS := $(NETDRV_OBJS) hp.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(hp.o)
 CONFIG_8390 = CONFIG_8390
 hp.o:	hp.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c hp.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
 endif
 
 ifdef CONFIG_ULTRA
-NETDRV_OBJS := $(NETDRV_OBJS) smc-ultra.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(smc-ultra.o)
+CONFIG_8390 = CONFIG_8390
+endif
+
+ifdef CONFIG_E2100
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(e2100.o)
+CONFIG_8390 = CONFIG_8390
+endif
+
+ifdef CONFIG_PLIP
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(plip.o)
+plip.o:	plip.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
+endif
+
+ifdef CONFIG_PPP
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ppp.o) net.a(slhc.o)
+endif
+
+ifdef CONFIG_SLIP
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(slip.o) net.a(slhc.o)
+slip.o:	slip.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(SLIP_OPTS) -c $<
+endif
+
+ifdef CONFIG_DE600
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(d_link.o)
+d_link.o: d_link.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c $<
+endif
+
+ifdef CONFIG_AT1500
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(lance.o)
+endif
+ifdef CONFIG_LANCE
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(lance.o)
+endif
+ifdef CONFIG_AT1700
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(at1700.o)
+endif
+ifdef CONFIG_EL1
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c501.o)
+endif
+ifdef CONFIG_EL16
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c507.o)
+endif
+ifdef CONFIG_EL3
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c509.o)
+endif
+ifdef CONFIG_EEXPRESS
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(eexpress.o)
+endif
+ifdef CONFIG_ZNET
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(znet.o)
+endif
+ifdef CONFIG_DEPCA
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(depca.o)
+endif
+ifdef CONFIG_ATP
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(atp.o)
+endif
+ifdef CONFIG_NI52
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ni52.o)
+endif
+ifdef CONFIG_NI65
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ni65.o)
+endif
+ifdef CONFIG_ELPLUS
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c505.o)
+endif
+ifdef CONFIG_AC3200
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ac3200.o)
 CONFIG_8390 = CONFIG_8390
 endif
 
 ifdef CONFIG_8390
-NETDRV_OBJS := $(NETDRV_OBJS) 8390.o
-endif
-
-ifdef CONFIG_PLIP
-NETDRV_OBJS := $(NETDRV_OBJS) plip.o
-plip.o:	plip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c plip.c
-endif
-
-ifdef CONFIG_SLIP
-NETDRV_OBJS := $(NETDRV_OBJS) slip.o slhc.o
-slip.o:	slip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(SLIP_OPTS) -c slip.c
-endif
-
-ifdef CONFIG_DE600
-NETDRV_OBJS := $(NETDRV_OBJS) d_link.o
-d_link.o: d_link.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c d_link.c
-endif
-
-ifdef CONFIG_AT1500
-NETDRV_OBJS := $(NETDRV_OBJS) lance.o
-endif
-
-ifdef CONFIG_EL3
-NETDRV_OBJS := $(NETDRV_OBJS) 3c509.o
-endif
-
-ifdef CONFIG_ZNET
-NETDRV_OBJS := $(NETDRV_OBJS) znet.o
-endif
-ifdef CONFIG_EEXPRESS
-NETDRV_OBJS := $(NETDRV_OBJS) eexpress.o
-endif
-ifdef CONFIG_EL1
-NETDRV_OBJS := $(NETDRV_OBJS) 3c501.o
-endif
-ifdef CONFIG_EL16
-NETDRV_OBJS := $(NETDRV_OBJS) 3c507.o
-endif
-ifdef CONFIG_DEPCA
-NETDRV_OBJS := $(NETDRV_OBJS) depca.o
-endif
-ifdef CONFIG_ATP
-NETDRV_OBJS := $(NETDRV_OBJS) atlantec.o
-endif
-ifdef CONFIG_NI52
-NETDRV_OBJS := $(NETDRV_OBJS) ni52.o
-endif
-ifdef CONFIG_NI65
-NETDRV_OBJS := $(NETDRV_OBJS) ni65.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(8390.o)
 endif
 
 ifdef CONFIG_IP_DEFRAG
-NETDRV_OBJS := $(NETDRV_OBJS) ip-frag.o
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(ip-frag.o)
 endif
 
 net.a: $(NETDRV_OBJS)
-	rm -f $@
-	$(AR) rcs $@ $^
+	ranlib net.a
 
 clean:
 	rm -f core *.o *.a *.s
@@ -130,11 +141,10 @@
 	$(CPP) -M *.c > .depend
 
 tar:
-	tar -cvf /dev/f1 .
 
-#
+
 # include a dependency file if one exists
-#
+
 ifeq (.depend,$(wildcard .depend))
 include .depend
 endif
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 64d8bb4..6cc4cf6 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -45,10 +45,18 @@
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int el3_probe(struct device *);
-extern int atp_probe(struct device *);
 extern int at1500_probe(struct device *);
+extern int at1700_probe(struct device *);
 extern int depca_probe(struct device *);
 extern int el1_probe(struct device *);
+extern int el16_probe(struct device *);
+extern int elplus_probe(struct device *);
+extern int ac3200_probe(struct device *);
+extern int e2100_probe(struct device *);
+
+/* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
+extern int atp_init(struct device *);
+extern int d_link_init(struct device *);
 
 static int
 ethif_probe(struct device *dev)
@@ -65,7 +73,7 @@
 #if defined(CONFIG_WD80x3) || defined(WD80x3)
 	&& wd_probe(dev)
 #endif
-#if defined(CONFIG_EL2) || defined(EL2)
+#if defined(CONFIG_EL2) || defined(EL2)	/* 3c503 */
 	&& el2_probe(dev)
 #endif
 #if defined(CONFIG_NE2000) || defined(NE2000)
@@ -77,24 +85,36 @@
 #ifdef CONFIG_AT1500
 	&& at1500_probe(dev)
 #endif
-#ifdef CONFIG_EL3
+#ifdef CONFIG_AT1700
+	&& at1700_probe(dev)
+#endif
+#ifdef CONFIG_EL3		/* 3c509 */
 	&& el3_probe(dev)
 #endif
-#ifdef CONFIG_ZNET
+#ifdef CONFIG_ZNET		/* Zenith Z-Note and some IBM Thinkpads. */
 	&& znet_probe(dev)
 #endif
-#ifdef CONFIG_EEXPRESS
+#ifdef CONFIG_EEXPRESS		/* Intel EtherExpress */
 	&& express_probe(dev)
 #endif
-#ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
-	&& atp_probe(dev)
-#endif
-#ifdef CONFIG_DEPCA
+#ifdef CONFIG_DEPCA		/* DEC DEPCA */
 	&& depca_probe(dev)
 #endif
-#ifdef CONFIG_EL1
+#ifdef CONFIG_EL1		/* 3c501 */
 	&& el1_probe(dev)
 #endif
+#ifdef CONFIG_EL16		/* 3c507 */
+	&& el16_probe(dev)
+#endif
+#ifdef CONFIG_ELPLUS		/* 3c505 */
+	&& elplus_probe(dev)
+#endif
+#ifdef CONFIG_AC3200		/* Ansel Communications EISA 3200. */
+	&& ac3200_probe(dev)
+#endif
+#ifdef CONFIG_E2100		/* Cabletron E21xx series. */
+	&& e2100_probe(dev)
+#endif
 	&& 1 ) {
 	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
     }
@@ -102,26 +122,22 @@
 }
 
 
-/* This remains seperate because it requires the addr and IRQ to be
-   set. */
+/* This remains seperate because it requires the addr and IRQ to be set. */
 #if defined(D_LINK) || defined(CONFIG_DE600)
-    extern int d_link_init(struct device *);
-    static struct device d_link_dev = {
-	"dl0",
-	0,
-	0,
-	0,
-	0,
-	D_LINK_IO,
-	D_LINK_IRQ,
-	0, 0, 0,
-	NEXT_DEV,
-	d_link_init
-    };
+static struct device d_link_dev = {
+    "dl0", 0, 0, 0, 0, D_LINK_IO, D_LINK_IRQ, 0, 0, 0, NEXT_DEV, d_link_init };
 #   undef NEXT_DEV
 #   define NEXT_DEV	(&d_link_dev)
 #endif
 
+/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
+#ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
+static struct device atp_dev = {
+    "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ };
+#   undef NEXT_DEV
+#   define NEXT_DEV	(&atp_dev)
+#endif
+
 /* The first device defaults to I/O base '0', which means autoprobe. */
 #ifdef EI8390
 # define ETH0_ADDR EI8390
@@ -214,7 +230,20 @@
 #   undef	NEXT_DEV
 #   define	NEXT_DEV	(&slip0_dev)
 #endif	/* SLIP */
-
+  
+#if defined(CONFIG_PPP)
+extern int ppp_init(struct device *);
+static struct device ppp3_dev = {
+    "ppp3", 0x0, 0x0, 0x0, 0x0, 3, 0, 0, 0, 0, NEXT_DEV,  ppp_init, };
+static struct device ppp2_dev = {
+    "ppp2", 0x0, 0x0, 0x0, 0x0, 2, 0, 0, 0, 0, &ppp3_dev, ppp_init, };
+static struct device ppp1_dev = {
+    "ppp1", 0x0, 0x0, 0x0, 0x0, 1, 0, 0, 0, 0, &ppp2_dev, ppp_init, };
+static struct device ppp0_dev = {
+    "ppp0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, &ppp1_dev, ppp_init, };
+#undef NEXT_DEV
+#define NEXT_DEV (&ppp0_dev)
+#endif   /* PPP */
 
 #ifdef LOOPBACK
     extern int loopback_init(struct device *dev);
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
new file mode 100644
index 0000000..fe619c8
--- /dev/null
+++ b/drivers/net/at1700.c
@@ -0,0 +1,661 @@
+/* at1700.c: A network device driver for  the Allied Telesis AT1700.
+
+   Written 1993 by Donald Becker.  This is a alpha test limited release.
+   This version may only be used and distributed according to the terms of the
+   GNU Public License, incorporated herein by reference.
+
+   The author may be reached as becker@super.org or
+   C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+   This is a device driver for the Allied Telesis AT1700, which is a
+   straight-foward Fujitsu MB86965 implementation.
+*/
+
+static char *version =
+	"at1700.c:v0.03 11/16/93  Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+
+/*
+  Sources:
+    The Fujitsu MB86695 datasheet.
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <errno.h>
+#include <memory.h>
+
+#include "dev.h"
+#include "iow.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c, in ioport.h for later versions. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+/* The map from IRQ number (as passed to the interrupt handler) to
+   'struct device'. */
+extern struct device *irq2dev_map[16];
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(addr, size) kfree_s(addr,size);
+#endif
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+typedef unsigned char uchar;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+	struct enet_statistics stats;
+	long open_time;				/* Useless example local info. */
+	uint tx_started:1;			/* Number of packet on the Tx queue. */
+	uchar tx_queue;				/* Number of packet on the Tx queue. */
+	ushort tx_queue_len;		/* Current length of the Tx queue. */
+};
+
+
+/* Offsets from the base address. */
+#define STATUS			0
+#define TX_STATUS		0
+#define RX_STATUS		1
+#define TX_INTR			2		/* Bit-mapped interrupt enable registers. */
+#define RX_INTR			3
+#define TX_MODE			4
+#define RX_MODE			5
+#define CONFIG_0		6		/* Misc. configuration settings. */
+#define CONFIG_1		7
+/* Run-time register bank 2 definitions. */
+#define DATAPORT		8		/* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START		10
+#define MODE13			13
+#define EEPROM_Ctrl 	16
+#define EEPROM_Data 	17
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x40	/* EEPROM shift clock, in reg. 16. */
+#define EE_CS			0x20	/* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE	0x80	/* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ	0x80	/* EEPROM chip data out, in reg. 17. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay()	do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ_CMD		(6 << 6)
+#define EE_ERASE_CMD	(7 << 6)
+
+
+/* Index to functions, as function prototypes. */
+
+extern int at1700_probe(struct device *dev);
+
+static int at1700_probe1(struct device *dev, short ioaddr);
+static int read_eeprom(int ioaddr, int location);
+static int net_open(struct device *dev);
+static int	net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int reg_ptr);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+#ifdef HAVE_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+   If dev->base_addr == 0, probe all likely locations.
+   If dev->base_addr == 1, always return failure.
+   If dev->base_addr == 2, alloate space for the device and return success
+   (detachable devices only).
+   */
+int
+at1700_probe(struct device *dev)
+{
+	short ports[] = {0x300, 0x280, 0x380, 0x320, 0x340, 0x260, 0x2a0, 0x240, 0};
+	short *port, base_addr = dev->base_addr;
+
+	if (base_addr > 0x1ff)		/* Check a single specified location. */
+		return at1700_probe1(dev, base_addr);
+	else if (base_addr > 0)		/* Don't probe at all. */
+		return ENXIO;
+
+	for (port = &ports[0]; *port; port++) {
+		int ioaddr = *port;
+#ifdef HAVE_PORTRESERVE
+		if (check_region(ioaddr, 32))
+			continue;
+#endif
+		if (inw(ioaddr) != 0x0000)
+			continue;
+		if (at1700_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+
+	return ENODEV;			/* ENODEV would be more accurate. */
+}
+
+int at1700_probe1(struct device *dev, short ioaddr)
+{
+	unsigned short signature[4]         = {0x0000, 0xffff, 0x41f6, 0xefb6};
+	unsigned short signature_invalid[4] = {0x0000, 0xffff, 0x00f0, 0x2f00};
+	char irqmap[4] = {3, 4, 5, 9};
+	unsigned short *station_address = (unsigned short *)dev->dev_addr;
+	unsigned int i, irq;
+
+	/* Resetting the chip doesn't reset the ISA interface, so don't bother.
+	   That means we have to be careful with the register values we probe for.
+	   */
+	for (i = 0; i < 4; i++)
+		if ((inw(ioaddr + 2*i) | signature_invalid[i]) != signature[i]) {
+			if (net_debug > 1)
+				printk("AT1700 signature match failed at %d (%04x vs. %04x)\n",
+					   i, inw(ioaddr + 2*i), signature[i]);
+			return -ENODEV;
+		}
+#ifdef HAVE_PORTRESERVE
+	/* Grab the region so that we can find another board if the IRQ request
+	   fails. */
+	snarf_region(ioaddr, 32);
+#endif
+
+	irq = irqmap[ read_eeprom(ioaddr, 0) >> 14 ];
+
+	/* Snarf the interrupt vector now. */
+	if (request_irq(irq, &net_interrupt)) {
+		printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
+				"IRQ %d.\n", ioaddr, irq);
+		return EAGAIN;
+	}
+
+	printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
+		   ioaddr, irq);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+	irq2dev_map[irq] = dev;
+
+	for(i = 0; i < 3; i++) {
+		unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+		printk("%04x", eeprom_val);
+		station_address[i] = ntohs(eeprom_val);
+	}
+
+	/* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
+	   rather than 150 ohm shielded twisted pair compansation.
+	   0x0000 == auto-sense the interface
+	   0x0800 == use TP interface
+	   0x1800 == use coax interface
+	   */
+	{
+		char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
+		ushort setup_value = read_eeprom(ioaddr, 12);
+
+		dev->if_port = setup_value >> 8;
+		printk(" %s interface (%04x).\n", porttype[(dev->if_port>>3) & 3],
+			   setup_value);
+	}
+
+	/* Set the station address in bank zero. */
+	outb(0xe0, ioaddr + 7);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+	/* Switch to bank 1 and set the multicast table to accept none. */
+	outb(0xe4, ioaddr + 7);
+	for (i = 0; i < 8; i++)
+		outb(0x00, ioaddr + 8 + i);
+
+	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+	   bus access, two 4K Tx queues, and disabled Tx and Rx. */
+	outb(0xda, ioaddr + CONFIG_0);
+
+	/* Switch to bank 2 and lock our I/O address. */
+	outb(0xe8, ioaddr + 7);
+	outb(dev->if_port, MODE13);
+
+	/* Power-down the chip.  Aren't we green! */
+	outb(0x00, ioaddr + CONFIG_1);
+
+	if (net_debug)
+		printk(version);
+
+	/* Initialize the device structure. */
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	dev->open		= net_open;
+	dev->stop		= net_close;
+	dev->hard_start_xmit = net_send_packet;
+	dev->get_stats	= net_get_stats;
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_multicast_list;
+#endif
+
+	/* 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++)
+		dev->buffs[i] = NULL;
+
+	dev->hard_header	= eth_header;
+	dev->add_arp		= eth_add_arp;
+	dev->queue_xmit		= dev_queue_xmit;
+	dev->rebuild_header	= eth_rebuild_header;
+	dev->type_trans		= eth_type_trans;
+
+	dev->type		= ARPHRD_ETHER;
+	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;
+	}
+
+	/* 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	= sizeof(unsigned long);
+
+	return 0;
+}
+
+static int read_eeprom(int ioaddr, int location)
+{
+	int i;
+	unsigned short retval = 0;
+	short ee_addr = ioaddr + EEPROM_Ctrl;
+	short ee_daddr = ioaddr + EEPROM_Data;
+	int read_cmd = location | EE_READ_CMD;
+	short ctrl_val = EE_CS;
+	
+	outb(ctrl_val, ee_addr);
+	
+	/* Shift the read command bits out. */
+	for (i = 9; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outb(dataval, ee_daddr);
+		outb(EE_CS | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick. */
+		eeprom_delay();
+		outb(EE_CS, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay();
+	}
+	outb(EE_CS, ee_addr);
+	
+	for (i = 16; i > 0; i--) {
+		outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
+		retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+		outb(EE_CS, ee_addr);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	ctrl_val &= ~EE_CS;
+	outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+	eeprom_delay();
+	outb(ctrl_val, ee_addr);
+	eeprom_delay();
+	return retval;
+}
+
+
+
+static int net_open(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int i;
+
+	/* Powerup the chip, initialize config register 1, and select bank 0. */
+	outb(0xe0, ioaddr + CONFIG_1);
+
+	/* Set the station address in bank zero. */
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+	/* Switch to bank 1 and set the multicast table to accept none. */
+	outb(0xe4, ioaddr + 7);
+	for (i = 0; i < 8; i++)
+		outb(0x00, ioaddr + 8 + i);
+
+	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+	   bus access, and two 4K Tx queues. */
+	outb(0xda, ioaddr + CONFIG_0);
+
+	/* Same config 0, except enable the Rx and Tx. */
+	outb(0x5a, ioaddr + CONFIG_0);
+	/* Switch to register bank 2 for the run-time registers. */
+	outb(0xe8, ioaddr + CONFIG_1);
+
+	/* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+	outb(0x00, ioaddr + TX_INTR);
+	outb(0x81, ioaddr + RX_INTR);
+
+	lp->open_time = jiffies;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	return 0;
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	if (dev->tbusy) {
+		/* If we get here, some higher level has decided we are broken.
+		   There should really be a "kick me" function call instead. */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 10)
+			return 1;
+		printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
+			   inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
+			   ? "IRQ conflict" : "network cable problem");
+		printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
+			   dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
+			   inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
+			   inw(ioaddr + 12), inw(ioaddr + 14));
+		lp->stats.tx_errors++;
+		/* ToDo: We should try to restart the adaptor... */
+		outw(0xffff, ioaddr + 24);
+		outw(0xffff, ioaddr + TX_STATUS);
+		outw(0xe85a, ioaddr + CONFIG_0);
+		outw(0x8100, ioaddr + TX_INTR);
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+	}
+
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself. */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	/* For ethernet, fill in the header.  This should really be done by a
+	   higher level, rather than duplicated for each ethernet adaptor. */
+	if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
+		skb->dev = dev;
+		arp_queue (skb);
+		return 0;
+	}
+	skb->arp=1;
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0)
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	else {
+		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		unsigned char *buf = (void *)(skb+1);
+
+		if (net_debug > 4)
+			printk("%s: Transmitting a packet of length %d.\n", dev->name,
+				   skb->len);
+
+		/* Turn off the possible Tx interrupts. */
+		outb(0x00, ioaddr + TX_INTR);
+		
+		outw(length, ioaddr + DATAPORT);
+		port_write(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+		lp->tx_queue++;
+		lp->tx_queue_len += length + 2;
+
+		if (lp->tx_started == 0) {
+			/* If the Tx is idle, always trigger a transmit. */
+			outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+			lp->tx_queue = 0;
+			lp->tx_queue_len = 0;
+			dev->trans_start = jiffies;
+			lp->tx_started = 1;
+		} else if (lp->tx_queue_len < 4096 - 1502)	/* Room for one more packet? */
+			dev->tbusy = 0;
+
+		/* Turn on Tx interrupts back on. */
+		outb(0x82, ioaddr + TX_INTR);
+	}
+	if (skb->free)
+		kfree_skb (skb, FREE_WRITE);
+
+	return 0;
+}
+
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void
+net_interrupt(int reg_ptr)
+{
+	int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct net_local *lp;
+	int ioaddr, status;
+
+	if (dev == NULL) {
+		printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	lp = (struct net_local *)dev->priv;
+	status = inw(ioaddr + TX_STATUS);
+	outw(status, ioaddr + TX_STATUS);
+
+	if (net_debug > 4)
+		printk("%s: Interrupt with status %04x.\n", dev->name, status);
+	if (status & 0xff00
+		||  (inb(ioaddr + RX_MODE) & 0x40) == 0) {			/* Got a packet(s). */
+		net_rx(dev);
+	}
+	if (status & 0x00ff) {
+		if (status & 0x80) {
+			lp->stats.tx_packets++;
+			if (lp->tx_queue) {
+				outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+				lp->tx_queue = 0;
+				lp->tx_queue_len = 0;
+				dev->trans_start = jiffies;
+				dev->tbusy = 0;
+				mark_bh(INET_BH);	/* Inform upper layers. */
+			} else {
+				lp->tx_started = 0;
+				/* Turn on Tx interrupts off. */
+				outb(0x00, ioaddr + TX_INTR);
+				dev->tbusy = 0;
+			}
+		}
+	}
+
+	return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int boguscount = 5;
+
+	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+		ushort status = inw(ioaddr + DATAPORT);
+
+		if (net_debug > 4)
+			printk("%s: Rxing packet mode %02x status %04x.\n",
+				   dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+		if (status == 0) {
+			outb(0x05, ioaddr + 14);
+			break;
+		}
+#endif
+
+		if ((status & 0xF0) != 0x20) {	/* There was an error. */
+			lp->stats.rx_errors++;
+			if (status & 0x08) lp->stats.rx_length_errors++;
+			if (status & 0x04) lp->stats.rx_frame_errors++;
+			if (status & 0x02) lp->stats.rx_crc_errors++;
+			if (status & 0x01) lp->stats.rx_over_errors++;
+		} else {
+			ushort pkt_len = inw(ioaddr + DATAPORT);
+			/* Malloc up new buffer. */
+			int sksize = sizeof(struct sk_buff) + pkt_len;
+			struct sk_buff *skb;
+
+			if (pkt_len > 1550) {
+				printk("%s: The AT1700 claimed a very large packet, size %d.\n",
+					   dev->name, pkt_len);
+				outb(0x05, ioaddr + 14);
+				lp->stats.rx_errors++;
+				break;
+			}
+			skb = alloc_skb(sksize, GFP_ATOMIC);
+			if (skb == NULL) {
+				printk("%s: Memory squeeze, dropping packet (len %d).\n",
+					   dev->name, pkt_len);
+				outb(0x05, ioaddr + 14);
+				lp->stats.rx_dropped++;
+				break;
+			}
+			skb->mem_len = sksize;
+			skb->mem_addr = skb;
+			skb->len = pkt_len;
+			skb->dev = dev;
+
+			/* 'skb+1' points to the start of sk_buff data area. */
+			port_read(ioaddr + DATAPORT, (void *)(skb+1), (pkt_len + 1) >> 1);
+
+			if (net_debug > 5) {
+				int i;
+				printk("%s: Rxed packet of length %d: ", dev->name, pkt_len);
+				for (i = 0; i < 14; i++)
+					printk(" %02x", ((unsigned char*)(skb + 1))[i]);
+				printk(".\n");
+			}
+
+#ifdef HAVE_NETIF_RX
+			netif_rx(skb);
+#else
+			skb->lock = 0;
+			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
+				kfree_s(skb, sksize);
+				lp->stats.rx_dropped++;
+				break;
+			}
+#endif
+			lp->stats.rx_packets++;
+		}
+		if (--boguscount <= 0)
+			break;
+	}
+
+	/* If any worth-while packets have been received, dev_rint()
+	   has done a mark_bh(INET_BH) for us and will work on them
+	   when we get to the bottom-half routine. */
+	{
+		int i;
+		for (i = 0; i < 20; i++) {
+			if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
+				break;
+			outb(0x05, ioaddr + 14);
+		}
+
+		if (net_debug > 5)
+			printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", 
+				   dev->name, inb(ioaddr + RX_MODE), i);
+	}
+	return;
+}
+
+/* The inverse routine to net_open(). */
+static int net_close(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	lp->open_time = 0;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Set configuration register 0 to disable Tx and Rx. */
+	outb(0xda, ioaddr + CONFIG_0);
+
+	/* Update the statistics -- ToDo. */
+
+	/* Power-down the chip.  Green, green, green! */
+	outb(0x00, ioaddr + CONFIG_1);
+
+	return 0;
+}
+
+/* Get the current statistics.	This may be called with the card open or
+   closed. */
+static struct enet_statistics *
+net_get_stats(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	cli();
+	/* ToDo: Update the statistics from the device registers. */
+	sti();
+
+	return &lp->stats;
+}
+
+#ifdef HAVE_MULTICAST
+/* Set or clear the multicast filter for this adaptor.
+   num_addrs == -1	Promiscuous mode, receive all packets
+   num_addrs == 0	Normal mode, clear multicast list
+   num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+			best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+	short ioaddr = dev->base_addr;
+	if (num_addrs) {
+		outw(3, ioaddr + RX_MODE);	/* Enable promiscuous mode */
+	} else
+		outw(2, ioaddr + RX_MODE);	/* Disable promiscuous, use normal mode */
+}
+#endif
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c at1700.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/auto_irq.c b/drivers/net/auto_irq.c
index e3be4cb..6fa11e1 100644
--- a/drivers/net/auto_irq.c
+++ b/drivers/net/auto_irq.c
@@ -73,13 +73,15 @@
     irqs_used |= ~irq_handled;
 
     /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
-    while (timeout >= jiffies)
+    while (timeout > jiffies)
 	;
 
     for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
 	if (irq_bitmap & irq_handled & mask) {
 	    irq_handled &= ~mask;
+#ifdef notdef
 	    printk(" Spurious interrupt on IRQ %d\n", i);
+#endif
 	    free_irq(i);
 	}
     }
@@ -92,7 +94,7 @@
     int timeout = jiffies+waittime;
 
     /* Hang out at least <waittime> jiffies waiting for the IRQ. */
-    while (timeout >= jiffies)
+    while (timeout > jiffies)
 	if (irq_number)
 	    break;
 
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 53c61bb..41c2480 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -19,7 +19,7 @@
 */
 
 static char *version =
-	"eexpress.c:v0.04 10/18/93 Donald Becker (becker@super.org)\n";
+	"eexpress.c:v0.06 10/27/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 
@@ -42,7 +42,6 @@
 #include <linux/ptrace.h>
 #include <linux/ioport.h>
 #include <linux/in.h>
-#include <linux/malloc.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -58,7 +57,9 @@
 
 #ifndef HAVE_ALLOC_SKB
 #define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#else
+/* This isn't quite right, but it's the best version define I can find right now. */
+#include <linux/malloc.h>
 #endif
 
 /* use 0 for production, 1 for verification, 2..7 for debug */
@@ -233,7 +234,7 @@
 
   */
 
-short init_words[] = {
+static short init_words[] = {
 	0x0000,					/* Set bus size to 16 bits. */
 	0x0000,0x0000,			/* Set control mailbox (SCB) addr. */
 	0,0,					/* pad to 0x000000. */
@@ -311,7 +312,7 @@
 express_probe(struct device *dev)
 {
 	/* Don't probe all settable addresses, 0x[23][0-7]0, just common ones. */
-	int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0};
+	int *port, ports[] = {0x300, 0x270, 0x320, 0x340, 0};
 	int base_addr = dev->base_addr;
 
 	if (base_addr > 0x1ff)	/* Check a single specified location. */
@@ -364,8 +365,6 @@
 	snarf_region(ioaddr, 16);
 	dev->base_addr = ioaddr;
 
-	outb(ASIC_RESET, ioaddr + EEPROM_Ctrl);
-
 	for (i = 0; i < 6; i++) {
 		dev->dev_addr[i] = ((unsigned char*)station_addr)[5-i];
 		printk(" %02x", dev->dev_addr[i]);
@@ -388,6 +387,9 @@
 		outb(0x00, ioaddr + SET_IRQ);
 	}
 
+	/* It's now OK to leave the board in reset, pending the open(). */
+	outb(ASIC_RESET, ioaddr + EEPROM_Ctrl);
+
 	if ((dev->mem_start & 0xf) > 0)
 		net_debug = dev->mem_start & 7;
 
@@ -447,12 +449,14 @@
 
 	if (dev->irq == 0  ||  irqrmap[dev->irq] == 0)
 		return -ENXIO;
-	if (request_irq(dev->irq, &eexp_interrupt)) {
+
+	if (irq2dev_map[dev->irq] != 0
+		/* This is always true, but avoid the false IRQ. */
+		|| (irq2dev_map[dev->irq] = dev) == 0
+		|| request_irq(dev->irq, &eexp_interrupt)) {
 		return -EAGAIN;
 	}
 
-	irq2dev_map[dev->irq] = dev;
-
 	/* Initialize the 82586 memory and start it. */
 	init_82586_mem(dev);
 
@@ -964,7 +968,7 @@
 #else
 			skb->lock = 0;
 			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_skbmem(skb, sksize);
+				kfree_s(skb, sksize);
 				lp->stats.rx_dropped++;
 				break;
 			}
diff --git a/drivers/net/el2.c b/drivers/net/el2.c
deleted file mode 100644
index 4ff9e31..0000000
--- a/drivers/net/el2.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/* el2.c: A shared-memory NS8390 ethernet driver for linux. */
-/*
-    Written 1992,1993 by Donald Becker.
-
-    Copyright 1993 United States Government as represented by the
-    Director, National Security Agency.  This software may be used and
-    distributed according to the terms of the GNU Public License,
-    incorporated herein by reference.
-
-    This driver should work with the 3c503 and 3c503/16.  It should be used
-    in shared memory mode for best performance, although it may also work
-    in programmed-I/O mode.
-
-    The Author may be reached as becker@super.org or
-    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
-*/
-
-static char *version =
-    "el2.c:v0.99.13 8/30/93 Donald Becker (becker@super.org)\n";
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include "dev.h"
-
-#include "8390.h"
-#include "el2reg.h"
-
-int el2_probe(struct device *dev);
-int el2_pio_autoprobe(struct device *dev);
-int el2probe1(int ioaddr, struct device *dev);
-
-static int el2_open(struct device *dev);
-static int el2_close(struct device *dev);
-static void el2_reset_8390(struct device *dev);
-static void el2_init_card(struct device *dev);
-static void el2_block_output(struct device *dev, int count,
-			     const unsigned char *buf, const start_page);
-static int el2_block_input(struct device *dev, int count, char *buf,
-			   int ring_offset);
-
-
-/* This routine probes for a memory-mapped 3c503 board by looking for
-   the "location register" at the end of the jumpered boot PROM space.
-   This works even if a PROM isn't there.
-
-   If the ethercard isn't found there is an optional probe for
-   ethercard jumpered to programmed-I/O mode.
-   */
-
-static int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
-
-int
-el2_probe(struct device *dev)
-{
-    int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
-    short ioaddr = dev->base_addr;
-
-    if (ioaddr < 0)
-	return ENXIO;		/* Don't probe at all. */
-    if (ioaddr > 0)
-	return ! el2probe1(ioaddr, dev);
-
-    for (addr = addrs; *addr; addr++) {
-	int i;
-	unsigned int base_bits = *(unsigned char *)*addr;
-	/* Find first set bit. */
-	for(i = 7; i >= 0; i--, base_bits >>= 1)
-	    if (base_bits & 0x1)
-		break;
-	if (base_bits != 1)
-	    continue;
-#ifdef HAVE_PORTRESERVE
-	if (check_region(ports[i], 16))
-	    continue;
-#endif
-	if (el2probe1(ports[i], dev))
-	    return 0;
-    }
-#ifndef no_probe_nonshared_memory
-    return el2_pio_autoprobe(dev);
-#else
-    return ENODEV;
-#endif
-}
-
-/*  Try all of the locations that aren't obviously empty.  This touches
-    a lot of locations, and is much riskier than the code above. */
-int
-el2_pio_autoprobe(struct device *dev)
-{
-    int i;
-    for (i = 0; i < 8; i++) {
-#ifdef HAVE_PORTRESERVE
-	if (check_region(ports[i], 16))
-	    continue;
-#endif
-	/* Reset and/or avoid any lurking NE2000 */
-	if (inb_p(ports[i] + 0x408) == 0xff)
-	    continue;
-	if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
-	    && el2probe1(ports[i], dev))
-	    return 0;
-    }
-    return ENODEV;
-}
-
-/* Probe for the Etherlink II card at I/O port base IOADDR,
-   returning non-zero on sucess.  If found, set the station
-   address and memory parameters in DEVICE. */
-int
-el2probe1(int ioaddr, struct device *dev)
-{
-    int i, iobase_reg, membase_reg, saved_406;
-    unsigned char *station_addr = dev->dev_addr;
-
-    /* We verify that it's a 3C503 board by checking the first three octets
-       of its ethernet address. */
-    printk("3c503 probe at %#3x:", ioaddr);
-    iobase_reg = inb(ioaddr+0x403);
-    membase_reg = inb(ioaddr+0x404);
-    /* Verify ASIC register that should be 0 or have a single bit set. */
-    if (   (iobase_reg  & (iobase_reg - 1))
-	|| (membase_reg & (membase_reg - 1))) {
-	printk("  not found.\n");
-	return 0;
-    }
-    saved_406 = inb_p(ioaddr + 0x406);
-    outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
-    outb_p(ECNTRL_THIN, ioaddr + 0x406);
-    /* Map the station addr PROM into the lower I/O ports. */
-    outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
-    for (i = 0; i < ETHER_ADDR_LEN; i++) {
-	printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
-    }
-    if ( station_addr[0] != 0x02
-	|| station_addr[1] != 0x60
-	|| station_addr[2] != 0x8c) {
-	printk("  3C503 not found.\n");
-	/* Restore the register we frobbed. */
-	outb(saved_406, ioaddr + 0x406);
-	return 0;
-    }
-
-#ifdef HAVE_PORTRESERVE
-    snarf_region(ioaddr, 16);
-#endif
-    ethdev_init(dev);
-
-    /* Map the 8390 back into the window. */
-    outb(ECNTRL_THIN, ioaddr + 0x406);
-    dev->base_addr = ioaddr;
-    /* Probe for, turn on and clear the board's shared memory. */
-    if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
-    outb(EGACFR_NORM, ioaddr + 0x405);	/* Enable RAM */
-
-    /* This should be probed for (or set via an ioctl()) at run-time.
-       Right now we use a sleazy hack to pass in the interface number
-       at boot-time via the low bits of the mem_end field.  That value is
-       unused, and the low bits would be discarded even if it was used. */
-#if defined(EI8390_THICK) || defined(EL2_AUI)
-    ei_status.interface_num = 1;
-#else
-    ei_status.interface_num = dev->mem_end & 0xf;
-#endif
-
-    if ((membase_reg & 0xf0) == 0) {
-	dev->mem_start = 0;
-    } else {
-	dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
-	    ((membase_reg & 0xA0) ? 0x4000 : 0);
-
-#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
-#ifdef EL2MEMTEST
-	/* This has never found an error, but someone might care. */
-	{			/* Check the card's memory. */
-	    int *mem_base = (int *)dev->mem_start;
-	    int memtest_value = 0xbbadf00d;
-	    mem_base[0] = 0xba5eba5e;
-	    for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
-		mem_base[i] = memtest_value;
-		if (mem_base[0] != 0xba5eba5e
-		    || mem_base[i] != memtest_value) {
-		    printk(" memory failure or memory address conflict.\n");
-		    dev->mem_start = 0;
-		    break;
-		}
-		memtest_value += 0x55555555;
-		mem_base[i] = 0;
-	    }
-	}
-#endif  /* EL2MEMTEST */
-	/* Divide the on-board memory into a single maximum-sized transmit
-	   (double-sized for ping-pong transmit) buffer at the base, and
-	   use the rest as a receive ring. */
-	dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
-	dev->rmem_start = TX_PAGES*256 + dev->mem_start;
-    }
-    if (ei_debug > 2)
-	printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
-	       dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
-
-    /* Finish setting the board's parameters. */
-    ei_status.name = "3C503";
-    ei_status.tx_start_page = EL2SM_START_PG;
-    ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
-    ei_status.stop_page = EL2SM_STOP_PG;
-    ei_status.reset_8390 = &el2_reset_8390;
-    ei_status.block_input = &el2_block_input;
-    ei_status.block_output = &el2_block_output;
-
-    if (dev->irq == 2)
-	dev->irq = 9;
-    else if (dev->irq > 5 && dev->irq != 9) {
-	printk("\n3c503: configured interrupt %d invalid, using autoIRQ.\n",
-	       dev->irq);
-	dev->irq = 0;
-    }
-
-    ei_status.saved_irq = dev->irq;
-
-    dev->start = 0;
-    dev->open = &el2_open;
-    dev->stop = &el2_close;
-    el2_init_card(dev);
-
-    if (dev->mem_start)
-	printk("\n%s: %s with shared memory at %#6x-%#6x,\n",
-	       dev->name, ei_status.name, dev->mem_start, dev->mem_end-1);
-    else
-	printk("\n%s: %s using programmed I/O (REJUMPER for SHARED MEMORY).\n",
-	       dev->name, ei_status.name);
-    if (ei_debug > 1)
-	printk(version);
-
-    return ioaddr;
-}
-
-static int
-el2_open(struct device *dev)
-{
-
-    if (dev->irq < 2) {
-	int irqlist[] = {5, 2, 3, 4, 0};
-	int *irqp = irqlist;
-
-	outb(EGACFR_NORM, E33G_GACFR);	/* Enable RAM and interrupts. */
-	do {
-	    if (request_irq (*irqp, NULL) != -EBUSY) {
-		/* Twinkle the interrupt, and check if it's seen. */
-		autoirq_setup(0);
-		outb_p(0x04 << *irqp, E33G_IDCFR);
-		outb_p(0x00, E33G_IDCFR);
-		if (*irqp == autoirq_report(0)	 /* It's a good IRQ line! */
-		    && request_irq (dev->irq = *irqp, &ei_interrupt) == 0)
-		    break;
-	    }
-	} while (*++irqp);
-	if (*irqp == 0) {
-	    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
-	    return -EAGAIN;
-	}
-    } else {
-	if (request_irq(dev->irq, &ei_interrupt)) {
-	    return -EAGAIN;
-	}
-    }
-    return ei_open(dev);
-}
-
-static int
-el2_close(struct device *dev)
-{
-    free_irq(dev->irq);
-    dev->irq = ei_status.saved_irq;
-    irq2dev_map[dev->irq] = NULL;
-    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
-
-    NS8390_init(dev, 0);
-
-    return 0;
-}
-
-/* This is called whenever we have a unrecoverable failure:
-       transmit timeout
-       Bad ring buffer packet header
- */
-static void
-el2_reset_8390(struct device *dev)
-{
-    if (ei_debug > 1) {
-	printk("%s: Resetting the 3c503 board...", dev->name);
-	printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
-	       E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
-    }
-    outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
-    ei_status.txing = 0;
-    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
-    el2_init_card(dev);
-    if (ei_debug > 1) printk("done\n");
-}
-
-/* Initialize the 3c503 GA registers after a reset. */
-static void
-el2_init_card(struct device *dev)
-{
-    /* Unmap the station PROM and select the DIX or BNC connector. */
-    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
-
-    /* Set ASIC copy of rx's first and last+1 buffer pages */
-    /* These must be the same as in the 8390. */
-    outb(ei_status.rx_start_page, E33G_STARTPG);
-    outb(ei_status.stop_page,  E33G_STOPPG);
-
-    /* Point the vector pointer registers somewhere ?harmless?. */
-    outb(0xff, E33G_VP2);	/* Point at the ROM restart location 0xffff0 */
-    outb(0xff, E33G_VP1);
-    outb(0x00, E33G_VP0);
-    /* Turn off all interrupts until we're opened. */
-    outb_p(0x00,  dev->base_addr + EN0_IMR);
-    /* Enable IRQs iff started. */
-    outb(EGACFR_NORM, E33G_GACFR);
-
-    /* Set the interrupt line. */
-    outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
-    outb_p(8, E33G_DRQCNT);		/* Set burst size to 8 */
-    outb_p(0x20, E33G_DMAAH);	/* Put a valid addr in the GA DMA */
-    outb_p(0x00, E33G_DMAAL);
-    return;			/* We always succeed */
-}
-
-/* Either use the shared memory (if enabled on the board) or put the packet
-   out through the ASIC FIFO.  The latter is probably much slower. */
-static void
-el2_block_output(struct device *dev, int count,
-		 const unsigned char *buf, const start_page)
-{
-    int i;				/* Buffer index */
-    int boguscount = 0;		/* timeout counter */
-
-    /* This should really be set with during an open(). */
-    outb(EGACFR_NORM, E33G_GACFR);	/* Enable RAM and interrupts. */
-
-    if (dev->mem_start) {	/* Shared memory transfer */
-	void *dest_addr = (void *)(dev->mem_start +
-	    ((start_page - ei_status.tx_start_page) << 8));
-	memcpy(dest_addr, buf, count);
-	if (ei_debug > 2  &&  memcmp(dest_addr, buf, count))
-	    printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
-		   dev->name, (int) dest_addr);
-	else if (ei_debug > 4)
-	    printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
-		   dev->name, (int) dest_addr);
-	return;
-    }
-    /* No shared memory, put the packet out the slow way. */
-    /* Set up then start the internal memory transfer to Tx Start Page */
-    outb(0x00, E33G_DMAAL);
-    outb_p(start_page, E33G_DMAAH);
-    outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
-	   | ECNTRL_START, E33G_CNTRL);
-
-    /* This is the byte copy loop: it should probably be tuned for
-       for speed once everything is working.  I think it is possible
-       to output 8 bytes between each check of the status bit. */
-    for(i = 0; i < count; i++) {
-	if (i % 8 == 0)
-	    while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
-		if (++boguscount > (i<<3) + 32) {
-		    printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
-			   dev->name, i, count, boguscount);
-		    return;
-		}
-	outb(buf[i], E33G_FIFOH);
-    }
-    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
-    return;
-}
-
-/* Returns the new ring pointer. */
-static int
-el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
-{
-    int boguscount = 0;
-    int end_of_ring = dev->rmem_end;
-    unsigned int i;
-
-    /* Maybe enable shared memory just be to be safe... nahh.*/
-    if (dev->mem_start) {	/* Use the shared memory. */
-	ring_offset -= (EL2SM_START_PG<<8);
-	if (dev->mem_start + ring_offset + count > end_of_ring) {
-	    /* We must wrap the input move. */
-	    int semi_count = end_of_ring - (dev->mem_start + ring_offset);
-	    if (ei_debug > 4)
-		printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
-		       dev->name, dev->mem_start, ring_offset,
-		       dev->mem_start + ring_offset);
-	    memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
-	    count -= semi_count;
-	    memcpy(buf + semi_count, (char *)dev->rmem_start, count);
-	    return dev->rmem_start + count;
-	}
-	if (ei_debug > 4)
-	    printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
-		   dev->name, dev->mem_start, ring_offset,
-		   dev->mem_start + ring_offset);
-	memcpy(buf, (char *)dev->mem_start + ring_offset, count);
-	return ring_offset + count;
-    }
-    /* No shared memory, use programmed I/O. */
-    outb(ring_offset & 0xff, E33G_DMAAL);
-    outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
-    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
-	   | ECNTRL_START, E33G_CNTRL);
-
-    /* This is the byte copy loop: it should probably be tuned for
-       for speed once everything is working. */
-    for(i = 0; i < count; i++) {
-	if (i % 8 == 0)
-	    while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
-		if (++boguscount > (i<<3) + 32) {
-		    printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
-			   dev->name, i, count, boguscount);
-		    boguscount = 0;
-		    break;
-		}
-	buf[i] = inb_p(E33G_FIFOH);
-    }
-    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
-    return 0;
-}
-
-/*
- * Local variables:
- *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
- *  version-control: t
- *  kept-new-versions: 5
- * End:
- */
diff --git a/drivers/net/el2reg.h b/drivers/net/el2reg.h
deleted file mode 100644
index a1c8575..0000000
--- a/drivers/net/el2reg.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Definitions for the 3Com 3c503 Etherlink 2. */
-/* This file is distributed under the GPL.
-   Some of these names and comments are from the Crynwr packet drivers. */
-
-#define EL2H (dev->base_addr + 0x400)
-#define EL2L (dev->base_addr)
-
-/* Shared memory management parameters */
-
-#define EL2SM_START_PG	(0x20)	/* First page of TX buffer */
-#define EL2SM_STOP_PG	(0x40)	/* Last page +1 of RX ring */
-
-/* 3Com 3c503 ASIC registers */
-#define E33G_STARTPG	(EL2H+0)       /* Start page, must match EN0_STARTPG */
-#define E33G_STOPPG	(EL2H+1)	/* Stop  page, must match EN0_STOPPG */
-#define E33G_DRQCNT	(EL2H+2)	/* DMA burst count */
-#define E33G_IOBASE	(EL2H+3)	/* Read of I/O base jumpers. */
-	/* (non-useful, but it also appears at the end of EPROM space) */
-#define E33G_ROMBASE	(EL2H+4)	/* Read of memory base jumpers. */
-#define E33G_GACFR	(EL2H+5)	/* Config/setup bits for the ASIC GA */
-#define E33G_CNTRL	(EL2H+6)	/* Board's main control register */
-#define E33G_STATUS	(EL2H+7)	/* Status on completions. */
-#define E33G_IDCFR	(EL2H+8)	/* Interrupt/DMA config register */
-				/* (Which IRQ to assert, DMA chan to use) */
-#define E33G_DMAAH	(EL2H+9)	/* High byte of DMA address reg */
-#define E33G_DMAAL	(EL2H+10)	/* Low byte of DMA address reg */
-/* "Vector pointer" - if this address matches a read, the EPROM (rather than
-   shared RAM) is mapped into memory space. */
-#define E33G_VP2	(EL2H+11)
-#define E33G_VP1	(EL2H+12)
-#define E33G_VP0	(EL2H+13)
-#define E33G_FIFOH	(EL2H+14)	/* FIFO for programmed I/O moves */
-#define E33G_FIFOL	(EL2H+15)	/* ... low byte of above. */
-
-/* Bits in E33G_CNTRL register: */
-
-#define ECNTRL_RESET	(0x01)	/* Software reset of the ASIC and 8390 */
-#define ECNTRL_THIN	(0x02)	/* Onboard xcvr enable, AUI disable */
-#define ECNTRL_AUI	(0x00)	/* Onboard xcvr disable, AUI enable */
-#define ECNTRL_SAPROM	(0x04)	/* Map the station address prom */
-#define ECNTRL_DBLBFR	(0x20)	/* FIFO configuration bit */
-#define ECNTRL_OUTPUT	(0x40)	/* PC-to-3C503 direction if 1 */
-#define ECNTRL_INPUT	(0x00)	/* 3C503-to-PC direction if 0 */
-#define ECNTRL_START	(0x80)	/* Start the DMA logic */
-
-/* Bits in E33G_STATUS register: */
-
-#define ESTAT_DPRDY	(0x80)	/* Data port (of FIFO) ready */
-#define ESTAT_UFLW	(0x40)	/* Tried to read FIFO when it was empty */
-#define ESTAT_OFLW	(0x20)	/* Tried to write FIFO when it was full */
-#define ESTAT_DTC	(0x10)	/* Terminal Count from PC bus DMA logic */
-#define ESTAT_DIP	(0x08)	/* DMA In Progress */
-
-/* Bits in E33G_GACFR register: */
-
-#define EGACFR_NORM	(0x49)	/* Enable 8K shared mem, no DMA TC int */
-#define EGACFR_IRQOFF	(0xc9)	/* Above, and disable 8390 IRQ line */
-
-/* End of 3C503 parameter definitions */
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index 3022808..b80ecbd 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -14,7 +14,7 @@
     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
 */
 
-static char *version = "lance.c:v0.13f 10/18/93 becker@super.org\n";
+static char *version = "lance.c:v0.13s 11/15/93 becker@super.org\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -24,6 +24,7 @@
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -34,6 +35,19 @@
 #include "skbuff.h"
 #include "arp.h"
 
+#ifndef HAVE_PORTRESERVE
+#define check_region(addr, size)	0
+#define snarf_region(addr, size)	do ; while(0)
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#endif
+
+struct device *init_etherdev(struct device *dev, int sizeof_private,
+			     unsigned long *mem_startp);
+
 #ifdef LANCE_DEBUG
 int lance_debug = LANCE_DEBUG;
 #else
@@ -44,21 +58,106 @@
 #define LANCE_DMA	5
 #endif
 
-/* Set the number of Tx and Rx buffers. */
-#ifndef LANCE_BUFFER_LOG_SZ
-#define RING_SIZE	16
-#define RING_MOD_MASK	0x0f
-#define RING_LEN_BITS	0x80000000
-#else
-#define RING_SIZE	(1 << (LANCE_BUFFERS_LOG_SZ))
-#define RING_MOD_MASK	(RING_SIZE - 1)
-#define RING_LEN_BITS	(LANCE_BUFFER_LOG_SZ << 13)
+/*
+  		Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the AMD 79C960, the "PCnet-ISA
+single-chip ethernet controller for ISA".  This chip is used in a wide
+variety of boards from vendors such as Allied Telesis, HP, Kingston,
+and Boca.  This driver is also intended to work with older AMD 7990
+designs, such as the NE1500 and NE2100.  For convenience, I use the name
+LANCE to refer to either AMD chip.
+
+II. Board-specific settings
+
+The driver is designed to work the boards that use the faster
+bus-master mode, rather than in shared memory mode.  (Only older designs
+have on-board buffer memory needed to support the slower shared memory mode.)
+
+Most boards have jumpered settings for the I/O base, IRQ line, and DMA channel.
+This driver probes the likely base addresses, {0x300, 0x320, 0x340, 0x360}.
+After the board is found it generates an DMA-timeout interrupt and uses
+autoIRQ to find the IRQ line.  The DMA channel defaults to LANCE_DMA, or it
+can be set with the low bits of the otherwise-unused dev->mem_start value.
+
+The HP-J2405A board is an exception: with this board it's easy to read the
+EEPROM-set values for the base, IRQ, and DMA.  Of course you must already
+_know_ the base address, but that entry is for changing the EEPROM.
+
+III. Driver operation
+
+IIIa. Ring buffers
+The LANCE uses ring buffers of Tx and Rx descriptors.  Each entry describes
+the base and length of the data buffer, along with status bits.  The length
+of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
+the buffer length (rather than being directly the buffer length) for
+implementation ease.  The current values are 2 (Tx) and 4 (Rx), which leads to
+ring sizes of 4 (Tx) and 16 (Rx).  Increasing the number of ring entries
+needlessly uses extra space and reduces the chance that an upper layer will
+be able to reorder queued Tx packets based on priority.  Decreasing the number
+of entries makes it more difficult to achieve back-to-back packet transmission
+and increases the chance that Rx ring will overflow.  (Consider the worst case
+of receiving back-to-back minimum-sized packets.)
+
+The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
+statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
+avoid the administrative overhead. For the Rx side this avoids dynamically
+allocating full-sized buffers "just in case", at the expense of a
+memory-to-memory data copy for each packet received.  For most systems this
+is an good tradeoff: the Rx buffer will always be in low memory, the copy
+is inexpensive, and it primes the cache for later packet processing.  For Tx
+the buffers are only used when needed as low-memory bounce buffers.
+
+IIIB. 16M memory limitations.
+For the ISA bus master mode all structures used directly by the LANCE,
+the initialization block, Rx and Tx rings, and data buffers, must be
+accessable from the ISA bus, i.e. in the lower 16M of real memory.
+This is a problem for current Linux kernels on >16M machines. The network
+devices are initialized after memory initialization, and the kernel doles out
+memory from the top of memory downward.  The current solution is to have a
+special network initialization routine that's called before memory
+initialization; this will eventually be generalized for all network devices.
+As mentioned before, low-memory "bounce-buffers" are used when needed.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.)  After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero.  Iff the 'lp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+*/
+
+/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
+   Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+   That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 2
+#define LANCE_LOG_RX_BUFFERS 4
 #endif
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif
+#define TX_RING_SIZE		(1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK	(TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS	((LANCE_LOG_TX_BUFFERS) << 29)
+
+#define RX_RING_SIZE		(1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK	(RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS	((LANCE_LOG_RX_BUFFERS) << 29)
+
+#define PKT_BUF_SZ	1544
 
 /* Offsets from base I/O address. */
 #define LANCE_DATA 0x10
@@ -81,7 +180,7 @@
 };
 
 /* The LANCE initialization block, described in databook. */
-struct lance_init {
+struct lance_init_block {
     unsigned short mode;	/* Pre-set mode (reg. 15) */
     unsigned char phys_addr[6];	/* Physical ethernet address */
     unsigned filter[2];		/* Multicast filter (unused). */
@@ -93,10 +192,12 @@
 struct lance_private {
     char devname[8];
     /* These must aligned on 8-byte boundaries. */
-    struct lance_rx_head rx_ring[RING_SIZE];
-    struct lance_tx_head tx_ring[RING_SIZE];
-    struct lance_init	init_block;
-    long dma_buffs;		/* Address of Rx and Tx buffers. */
+    struct lance_rx_head rx_ring[RX_RING_SIZE];
+    struct lance_tx_head tx_ring[TX_RING_SIZE];
+    struct lance_init_block	init_block;
+    long rx_buffs;		/* Address of Rx and Tx buffers. */
+    /* Tx low-memory "bounce buffer" address. */
+    char (*tx_bounce_buffs)[PKT_BUF_SZ];
     int	cur_rx, cur_tx;		/* The next free ring entry */
     int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
     int dma;
@@ -105,18 +206,7 @@
     int pad0, pad1;		/* Used for alignment */
 };
 
-/* We need a ethercard low-memory allocation scheme for for bus-master or
-   DMA ethercards if they are to work >16M memory systems. This is a
-   temporary solution to the lack of one, but it limits us to a single
-   AT1500 and <16M. Bummer. */
-
-#define PKT_BUF_SZ	1544
-
-#ifndef MEM_START_ALLOC
-static char rx_buffs[PKT_BUF_SZ][RING_SIZE];
-#endif
-
-static int lance_probe1(struct device *dev, short ioaddr);
+static unsigned long lance_probe1(short ioaddr, unsigned long mem_start);
 static int lance_open(struct device *dev);
 static void lance_init_ring(struct device *dev);
 static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -130,41 +220,27 @@
 
 
 
-int at1500_probe(struct device *dev)
+unsigned long lance_init(unsigned long mem_start, unsigned long mem_end)
 {
     int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
-    int base_addr = dev->base_addr;
 
-    if (base_addr > 0x1ff)		/* Check a single specified location. */
-		return lance_probe1(dev, base_addr);
-    else if (base_addr > 0)		/* Don't probe at all. */
-		return ENXIO;
-
-    /* First probe for the ethercard ID, 0x57, and then look for a LANCE
-       chip. */
-    
+    printk("lance_init(%#x, %#x).\n", mem_start, mem_end);
     for (port = &ports[0]; *port; port++) {
 	int ioaddr = *port;
 
-#ifdef HAVE_PORTRESERVE
-	if (check_region(ioaddr, LANCE_TOTAL_SIZE))
-	    continue;
-#endif
-	if (inb(ioaddr + 14) != 0x57
-	    || inb(ioaddr + 15) != 0x57)
-	    continue;
-
-	if (lance_probe1(dev, ioaddr) == 0)
-	    return 0;
+	if (   check_region(ioaddr, LANCE_TOTAL_SIZE) == 0
+	    && inb(ioaddr + 14) == 0x57
+	    && inb(ioaddr + 15) == 0x57) {
+	    mem_start = lance_probe1(ioaddr, mem_start);
+	}
     }
 
-    dev->base_addr = base_addr;
-    return ENODEV;			/* ENODEV would be more accurate. */
+    return mem_start;
 }
 
-static int
-lance_probe1(struct device *dev, short ioaddr)
+static unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
 {
+    struct device *dev;
     struct lance_private *lp;
     int hpJ2405A = 0;
     int i, reset_val;
@@ -182,7 +258,11 @@
 
     outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
     if (inw(ioaddr+LANCE_DATA) != 0x0004)
-	return ENXIO;
+	return mem_start;
+
+    dev = init_etherdev(0, sizeof(struct lance_private)
+			+ PKT_BUF_SZ*(RX_RING_SIZE + TX_RING_SIZE),
+			&mem_start);
 
     printk("%s: LANCE at %#3x,", dev->name, ioaddr);
 
@@ -192,43 +272,27 @@
 	printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
 
     dev->base_addr = ioaddr;
-#ifdef HAVE_PORTRESERVE
     snarf_region(ioaddr, LANCE_TOTAL_SIZE);
-#endif
 
-    /* Make up a LANCE-specific-data structure. */
-#ifdef MEM_START_ALLOC
-    dev->priv = (void *)((mem_start + 7) & ~7);
-    mem_start += (long)dev->priv + sizeof(struct lance_private);
+    /* Make certain the data structures used by the LANCE are aligned. */
+    dev->priv = (void *)(((int)dev->priv + 7) & ~7);
     lp = (struct lance_private *)dev->priv;
-    lp->dma_buffs = mem_start;
-    mem_start += PKT_BUF_SZ * RING_SIZE;
-#else    
-    dev->priv = kmalloc(sizeof(struct lance_private), GFP_KERNEL);
-    /* Align on 8-byte boundary. */
-    dev->priv = (void *)(((int)dev->priv + 7) & ~0x07);
-
-    if ((int)dev->priv & 0xff000000  ||  (int) rx_buffs & 0xff000000) {
-	printk(" disabled (buff %#x > 16M).\n", (int)rx_buffs);
-	return -ENOMEM;
-    }
-    memset(dev->priv, 0, sizeof(struct lance_private));
-    lp = (struct lance_private *)dev->priv;
-    lp->dma_buffs = (long)rx_buffs;
-#endif
+    lp->rx_buffs = (long)dev->priv + sizeof(struct lance_private);
+    lp->tx_bounce_buffs = (char (*)[PKT_BUF_SZ])
+			   (lp->rx_buffs + PKT_BUF_SZ*RX_RING_SIZE);
 
 #ifndef final_version
     /* This should never happen. */
     if ((int)(lp->rx_ring) & 0x07) {
 	printk(" **ERROR** LANCE Rx and Tx rings not on even boundary.\n");
-	return -ENXIO;
+	return mem_start;
     }
 #endif
 
     outw(88, ioaddr+LANCE_ADDR);
     lp->old_lance = (inw(ioaddr+LANCE_DATA) != 0x3003);
 
-#ifdef notdef
+#if defined(notdef)
     printk(lp->old_lance ? " original LANCE (%04x)" : " PCnet-ISA LANCE (%04x)",
 	   inw(ioaddr+LANCE_DATA));
 #endif
@@ -238,8 +302,8 @@
 	lp->init_block.phys_addr[i] = dev->dev_addr[i];
     lp->init_block.filter[0] = 0x00000000;
     lp->init_block.filter[1] = 0x00000000;
-    lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
-    lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
+    lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+    lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
 
     outw(0x0001, ioaddr+LANCE_ADDR);
     outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
@@ -256,11 +320,15 @@
 	printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
     } else {
 	/* The DMA channel may be passed in on this parameter. */
-	dev->dma = dev->mem_start & 0x07;
+	if (dev->mem_start & 0x07)
+	    dev->dma = dev->mem_start & 0x07;
+	else if (dev->dma == 0)
+	    dev->dma = LANCE_DMA;
 
 	/* To auto-IRQ we enable the initialization-done and DMA err,
 	   interrupts. For now we will always get a DMA error. */
 	if (dev->irq < 2) {
+
 	    autoirq_setup(0);
 
 	    /* Trigger an initialization just for the interrupt. */
@@ -271,7 +339,7 @@
 		printk(", probed IRQ %d, fixed at DMA %d.\n", dev->irq, dev->dma);
 	    else {
 		printk(", failed to detect IRQ line.\n");
-		return -EAGAIN;
+		return mem_start;
 	    }
 	} else
 	    printk(" assigned IRQ %d DMA %d.\n", dev->irq, dev->dma);
@@ -296,35 +364,7 @@
     dev->set_multicast_list = &set_multicast_list;
 #endif
 
-    dev->mem_start = 0;
-
-    /* Fill in the generic field of the device structure. */
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-	dev->buffs[i] = NULL;
-
-    dev->hard_header	= eth_header;
-    dev->add_arp	= eth_add_arp;
-    dev->queue_xmit	= dev_queue_xmit;
-    dev->rebuild_header	= eth_rebuild_header;
-    dev->type_trans	= eth_type_trans;
-
-    dev->type		= ARPHRD_ETHER;
-    dev->hard_header_len = ETH_HLEN;
-    dev->mtu		= 1500; /* eth_mtu */
-    dev->addr_len	= ETH_ALEN;
-    for (i = 0; i < dev->addr_len; i++) {
-	dev->broadcast[i]=0xff;
-    }
-
-    /* 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	= sizeof(unsigned long);
-
-    return 0;
+    return mem_start;
 }
 
 
@@ -339,9 +379,7 @@
 	return -EAGAIN;
     }
 
-    lp->dma = dev->dma ? dev->dma : LANCE_DMA;
-
-    if (request_dma(lp->dma)) {
+    if (request_dma(dev->dma)) {
 	free_irq(dev->irq);
 	return -EAGAIN;
     }
@@ -351,8 +389,8 @@
     inw(ioaddr+LANCE_RESET);
 
     /* The DMA controller is used as a no-operation slave, "cascade mode". */
-    enable_dma(lp->dma);
-    set_dma_mode(lp->dma, DMA_MODE_CASCADE);
+    enable_dma(dev->dma);
+    set_dma_mode(dev->dma, DMA_MODE_CASCADE);
 
     /* Un-Reset the LANCE, needed only for the NE2100. */
     if (lp->old_lance)
@@ -366,7 +404,7 @@
 
     if (lance_debug > 1)
 	printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
-	       dev->name, dev->irq, lp->dma, (int) lp->tx_ring, (int) lp->rx_ring,
+	       dev->name, dev->irq, dev->dma, (int) lp->tx_ring, (int) lp->rx_ring,
 	       (int) &lp->init_block);
 
     lance_init_ring(dev);
@@ -408,9 +446,13 @@
     lp->cur_rx = lp->cur_tx = 0;
     lp->dirty_rx = lp->dirty_tx = 0;
 
-    for (i = 0; i < RING_SIZE; i++) {
-	lp->rx_ring[i].base = (lp->dma_buffs + i*PKT_BUF_SZ) | 0x80000000;
+    for (i = 0; i < RX_RING_SIZE; i++) {
+	lp->rx_ring[i].base = (lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
 	lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+    }
+    /* The Tx buffer address is filled in as needed, but we do need to clear
+       the upper ownership bit. */
+    for (i = 0; i < TX_RING_SIZE; i++) {
 	lp->tx_ring[i].base = 0;
     }
 
@@ -419,8 +461,8 @@
 	lp->init_block.phys_addr[i] = dev->dev_addr[i];
     lp->init_block.filter[0] = 0x00000000;
     lp->init_block.filter[1] = 0x00000000;
-    lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
-    lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
+    lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+    lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
 }
 
 static int
@@ -428,18 +470,36 @@
 {
     struct lance_private *lp = (struct lance_private *)dev->priv;
     int ioaddr = dev->base_addr;
+    int entry;
 
     /* Transmitter timeout, serious problems. */
     if (dev->tbusy) {
 	int tickssofar = jiffies - dev->trans_start;
-	if (tickssofar < 5)
+	if (tickssofar < 10)
 	    return 1;
 	outw(0, ioaddr+LANCE_ADDR);
 	printk("%s: transmit timed out, status %4.4x, resetting.\n",
 	       dev->name, inw(ioaddr+LANCE_DATA));
-
+	outw(0x0001, ioaddr+LANCE_DATA);
+	lp->stats.tx_errors++;
+#ifndef final_version
+	{
+	    int i;
+	    printk(" Ring data dump: dirty_tx %d cur_tx %d cur_rx %d.",
+		   lp->dirty_tx, lp->cur_tx, lp->cur_rx);
+	    for (i = 0 ; i < RX_RING_SIZE; i++)
+		printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+		       lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+		       lp->rx_ring[i].msg_length);
+	    for (i = 0 ; i < TX_RING_SIZE; i++)
+		printk(" %s%08x %04x %04x", i & 0x3 ? "" : "\n ",
+		       lp->tx_ring[i].base, -lp->tx_ring[i].length,
+		       lp->tx_ring[i].misc);
+	    printk("\n");
+	}
+#endif
 	lance_init_ring(dev);
-	outw(0x43, ioaddr+LANCE_DATA);
+	outw(0x0043, ioaddr+LANCE_DATA);
 
 	dev->tbusy=0;
 	dev->trans_start = jiffies;
@@ -475,49 +535,47 @@
     if (set_bit(0, (void*)&dev->tbusy) != 0)
 	printk("%s: Transmitter access conflict.\n", dev->name);
 
-    /* This code is broken for >16M RAM systems.
-       There are two ways to fix it:
-           Keep around several static low-memory buffers, and deal with
-	   the book-keeping (bad for small systems).
-	   Make static Tx buffers that are optionally used.
-	   (Even worse.)
-     */
-    {				/* Fill in a Tx ring entry */
-	int entry = lp->cur_tx++;
+    /* Fill in a Tx ring entry */
 
-	entry &= RING_MOD_MASK;		/* Ring buffer. */
-	/* Caution: the write order is important here. */
-	lp->tx_ring[entry].length = -skb->len;
+    /* Mask to ring buffer boundary. */
+    entry = lp->cur_tx & TX_RING_MOD_MASK;
 
-	/* This shouldn't be necessary... */
+    /* Caution: the write order is important here, set the base address
+       with the "ownership" bits last. */
+
+    /* The old LANCE chips doesn't automatically pad buffers to min. size. */
+    if (lp->old_lance) {
 	lp->tx_ring[entry].length =
 	    -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+    } else
+	lp->tx_ring[entry].length = -skb->len;
 
-	lp->tx_ring[entry].misc = 0x0000;
+    lp->tx_ring[entry].misc = 0x0000;
+
+    /* If any part of this buffer is >16M we must copy it to a low-memory
+       buffer. */
+    if ((int)(skb+1) + skb->len > 0x01000000) {
+	if (lance_debug > 5)
+	    printk("%s: bouncing a high-memory packet (%#x).\n",
+		   dev->name, (int)(skb+1));
+	memcpy(&lp->tx_bounce_buffs[entry], skb+1, skb->len);
+	lp->tx_ring[entry].base =
+	    (int)(lp->tx_bounce_buffs + entry) | 0x83000000;
+	if (skb->free)
+	    kfree_skb (skb, FREE_WRITE);
+    } else
 	lp->tx_ring[entry].base = (int)(skb+1) | 0x83000000;
 
-	/* Trigger an immediate send poll. */
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	outw(0x0048, ioaddr+LANCE_DATA);
+    lp->cur_tx++;
 
-	if (lance_debug > 4) {
-	    unsigned char *pkt =
-		(unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
+    /* Trigger an immediate send poll. */
+    outw(0x0000, ioaddr+LANCE_ADDR);
+    outw(0x0048, ioaddr+LANCE_DATA);
 
-	    printk("%s: tx ring[%d], %#x, sk_buf %#x len %d.\n",
-		   dev->name, entry, (int) &lp->tx_ring[entry],
-		   lp->tx_ring[entry].base, -lp->tx_ring[entry].length);
-	    printk("%s:  Tx %2.2x %2.2x %2.2x ... %2.2x  %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x  %2.2x %2.2x.\n",
-		   dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6],
-		   pkt[7], pkt[8], pkt[11], pkt[12], pkt[13],
-		   pkt[14], pkt[15]);
-	}
+    dev->trans_start = jiffies;
 
-	dev->trans_start = jiffies;
-
-	if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0)
-	    dev->tbusy=0;
-    }
+    if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+	dev->tbusy=0;
 
     return 0;
 }
@@ -557,41 +615,65 @@
 	lance_rx(dev);
 
     if (csr0 & 0x0200) {	/* Tx-done interrupt */
-	int dirty_tx = lp->dirty_tx & RING_MOD_MASK;
+	int dirty_tx = lp->dirty_tx;
 
-	if (lance_debug > 5)
-	    printk("%s: Cleaning tx ring, dirty %d clean %d.\n",
-		   dev->name, dirty_tx, (lp->cur_tx & RING_MOD_MASK));
+	if (dirty_tx == lp->cur_tx - TX_RING_SIZE
+	    && dev->tbusy) {
+	    /* The ring is full, clear tbusy. */
+	    dev->tbusy = 0;
+	    mark_bh(INET_BH);
+	}
 
-	/* This code is broken for >16M RAM systems. */
-	while (dirty_tx != (lp->cur_tx & RING_MOD_MASK)
-	       && lp->tx_ring[dirty_tx].base > 0) {
-	    struct sk_buff *skb =
-		(struct sk_buff *)(lp->tx_ring[dirty_tx].base & 0x00ffffff);
-	    unsigned short *tmdp = (unsigned short *)(&lp->tx_ring[dirty_tx]);
-	    int status = lp->tx_ring[dirty_tx].base >> 24;
+	while (dirty_tx < lp->cur_tx) {
+	    int entry = dirty_tx & TX_RING_MOD_MASK;
+	    int status = lp->tx_ring[entry].base;
+	    void *databuff;
+	    
+	    if (status < 0)
+		break;		/* It still hasn't been Txed */
 
-	    if (status & 0x40) { /* There was an major error, log it. */
-		int err_status = lp->tx_ring[dirty_tx].misc;
+	    lp->tx_ring[entry].base = 0;
+	    databuff = (void*)(status & 0x00ffffff);
+
+	    if (status & 0x40000000) { /* There was an major error, log it. */
+		int err_status = lp->tx_ring[entry].misc;
 		lp->stats.tx_errors++;
 		if (err_status & 0x0400) lp->stats.tx_aborted_errors++;
 		if (err_status & 0x0800) lp->stats.tx_carrier_errors++;
 		if (err_status & 0x1000) lp->stats.tx_window_errors++;
 		if (err_status & 0x4000) lp->stats.tx_fifo_errors++;
 		/* We should re-init() after the FIFO error. */
-	    } else if (status & 0x18)
+	    } else if (status & 0x18000000)
 		lp->stats.collisions++;
 	    else
 		lp->stats.tx_packets++;
-	    if (lance_debug > 5)
-		printk("%s: Tx done entry %d, %4.4x %4.4x %4.4x %4.4x.\n",
-		       dev->name, dirty_tx,
-		       tmdp[0], tmdp[1], tmdp[2], tmdp[3]);
-	    if ((skb-1)->free)
-		kfree_skb (skb-1, FREE_WRITE);
-	    dirty_tx = ++lp->dirty_tx & RING_MOD_MASK;
+
+	    /* We don't free the skb if it's a data-only copy in the bounce
+	       buffer.  The address checks here are sorted -- the first test
+	       should always works.  */
+	    if (databuff >= (void*)(&lp->tx_bounce_buffs[TX_RING_SIZE])
+		|| databuff < (void*)(lp->tx_bounce_buffs)) {
+		struct sk_buff *skb = ((struct sk_buff *)databuff) - 1;
+		if (skb->free)
+		    kfree_skb(skb, FREE_WRITE);
+	    }
+	    dirty_tx++;
 	}
-	/* mark_bh(INET_BH); */
+
+	lp->dirty_tx = dirty_tx;
+
+#ifndef final_version
+	if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+	    printk("out-of-sync dirty pointer, %d vs. %d.\n",
+		   dirty_tx, lp->cur_tx);
+	    lp->dirty_tx += TX_RING_SIZE;
+	}
+#endif
+    }
+
+    if (csr0 & 0x8000) {
+	if (csr0 & 0x4000) lp->stats.tx_errors++;
+	if (csr0 & 0x1000) lp->stats.rx_errors++;
     }
 
     /* Clear the interrupts we've handled. */
@@ -611,7 +693,7 @@
 lance_rx(struct device *dev)
 {
     struct lance_private *lp = (struct lance_private *)dev->priv;
-    int entry = lp->cur_rx & RING_MOD_MASK;
+    int entry = lp->cur_rx & RX_RING_MOD_MASK;
 	
     /* If we own the next entry, it's a new packet. Send it up. */
     while (lp->rx_ring[entry].base >= 0) {
@@ -656,14 +738,12 @@
 	}
 
 	lp->rx_ring[entry].base |= 0x80000000;
-	entry = (entry+1) & RING_MOD_MASK;
+	entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
     }
 
     /* We should check that at least two ring entries are free.  If not,
        we should free one and mark stats->rx_dropped++. */
 
-    lp->cur_rx = entry;
-
     return 0;
 }
 
@@ -689,10 +769,10 @@
        memory if we don't. */
     outw(0x0004, ioaddr+LANCE_DATA);
 
-    disable_dma(lp->dma);
+    disable_dma(dev->dma);
 
     free_irq(dev->irq);
-    free_dma(lp->dma);
+    free_dma(dev->dma);
 
     irq2dev_map[dev->irq] = 0;
 
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index 86cfec2..95d4231 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -17,7 +17,7 @@
 /* Routines for the NatSemi-based designs (NE[12]000). */
 
 static char *version =
-    "ne.c:v0.99-13 8/30/93 Donald Becker (becker@super.org)\n";
+    "ne.c:v0.99-13s 11/17/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -132,7 +132,7 @@
        We can't reliably read the SAPROM address without this.
        (I learned the hard way!). */
     {
-	struct {char value, offset; } program_seq[] = {
+	struct {unsigned char value, offset; } program_seq[] = {
 	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
 	    {0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
 	    {0x00,	EN0_RCNTLO},	/* Clear the count regs. */
@@ -141,7 +141,7 @@
 	    {0xFF,	EN0_ISR},
 	    {E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
 	    {E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
-    	    {32,		EN0_RCNTLO},
+	    {32,	EN0_RCNTLO},
 	    {0x00,	EN0_RCNTHI},
 	    {0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
 	    {0x00,	EN0_RSARHI},
@@ -190,7 +190,7 @@
 	    start_page = NESM_START_PG;
 	    stop_page = NESM_STOP_PG;
 	} else {
-	    name = dlink ? "DE100" : "D-Link";
+	    name = dlink ? "DE100" : "NE1000";
 	    start_page = NE1SM_START_PG;
 	    stop_page = NE1SM_STOP_PG;
 	}
diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c
new file mode 100644
index 0000000..4c15147
--- /dev/null
+++ b/drivers/net/net_init.c
@@ -0,0 +1,163 @@
+/* netdrv_init.c: Initialization for network devices. */
+/*
+	Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the Director,
+	National Security Agency.  This software may only be used and distributed
+	according to the terms of the GNU Public License as modified by SRC,
+	incorported herein by reference.
+
+	The author may be reached as becker@super.org or
+	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+	This file contains the initialization for the "pl14+" style ethernet
+	drivers.  It should eventually replace most of drivers/net/Space.c.
+	It's primary advantage is that it's able to allocate low-memory buffers.
+	A secondary advantage is that the dangerous NE*000 netcards can reserve
+	their I/O port region before the SCSI probes start.
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/if_ether.h>
+#include <memory.h>
+#include "dev.h"
+#include "eth.h"
+
+/* The network devices currently exist only in the socket namespace, so these
+   entries are unused.  The only ones that make sense are
+    open	start the ethercard
+    close	stop  the ethercard
+    ioctl	To get statistics, perhaps set the interface port (AUI, BNC, etc.)
+   One can also imagine getting raw packets using
+    read & write
+   but this is probably better handled by a raw packet socket.
+
+   Given that almost all of these functions are handled in the current
+   socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+*/
+
+/* The next device number/name to assign: "eth0", "eth1", etc. */
+static int next_ethdev_number = 0;
+
+#ifdef NET_MAJOR_NUM
+static struct file_operations netcard_fops = {
+	NULL,		/* lseek */
+	NULL,		/* read */
+	NULL,		/* write */
+	NULL,		/* readdir */
+	NULL,		/* select */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	NULL,		/* open */
+	NULL,		/* release */
+	NULL		/* fsync */
+};
+#endif
+
+unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
+
+/*
+  net_dev_init() is our network device initialization routine.
+  It's called from init/main.c with the start and end of free memory,
+  and returns the new start of free memory.
+  */
+
+unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
+{
+
+#ifdef NET_MAJOR_NUM
+	if (register_chrdev(NET_MAJOR_NUM, "network",&netcard_fops))
+		printk("WARNING: Unable to get major %d for the network devices.\n",
+			   NET_MAJOR_NUM);
+#endif
+
+#if defined(CONFIG_LANCE)			/* Note this is _not_ CONFIG_AT1500. */
+	mem_start = lance_init(mem_start, mem_end);
+#endif
+
+	return mem_start;
+}
+
+/* Fill in the fields of the device structure with ethernet-generic values.
+
+   If no device structure is passed, a new one is constructed, complete with
+   a SIZEOF_PRIVATE private data area.
+
+   If an empty string area is passed as dev->name, or a new structure is made,
+   a new name string is constructed.  The passed string area should be 8 bytes
+   long.
+ */
+
+struct device *init_etherdev(struct device *dev, int sizeof_private,
+							 unsigned long *mem_startp)
+{
+	int i;
+	int new_device = 0;
+
+	if (dev == NULL) {
+		int alloc_size = sizeof(struct device) + sizeof("eth%d ")
+			+ sizeof_private;
+		if (mem_startp && *mem_startp ) {
+			dev = (struct device *)*mem_startp;
+			*mem_startp += alloc_size;
+		} else
+			dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
+		memset(dev, 0, sizeof(alloc_size));
+		dev->name = (char *)(dev + 1);
+		if (sizeof_private)
+			dev->priv = dev->name + sizeof("eth%d ");
+		new_device = 1;
+	}
+
+	if (dev->name  &&  dev->name[0] == '\0')
+		sprintf(dev->name, "eth%d", next_ethdev_number++);
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		dev->buffs[i] = NULL;
+	
+	dev->hard_header	= eth_header;
+	dev->add_arp		= eth_add_arp;
+	dev->queue_xmit		= dev_queue_xmit;
+	dev->rebuild_header	= eth_rebuild_header;
+	dev->type_trans		= eth_type_trans;
+	
+	dev->type			= ARPHRD_ETHER;
+	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;
+	}
+	
+	/* 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		= sizeof(unsigned long);
+	
+	if (new_device) {
+		/* Append the device to the device queue. */
+		struct device **old_devp = &dev_base;
+		while ((*old_devp)->next)
+			old_devp = & (*old_devp)->next;
+		(*old_devp)->next = dev;
+		dev->next = 0;
+	}
+	return dev;
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/skeleton.c b/drivers/net/skeleton.c
index b6440d8..48d64a8 100644
--- a/drivers/net/skeleton.c
+++ b/drivers/net/skeleton.c
@@ -4,7 +4,7 @@
 	Copyright 1993 United States Government as represented by the Director,
 	National Security Agency.  This software may only be used and distributed
 	according to the terms of the GNU Public License as modified by SRC,
-	incorported herein by reference.
+	incorporated herein by reference.
 
 	The author may be reached as becker@super.org or
 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
@@ -19,7 +19,7 @@
 */
 
 static char *version =
-	"skeleton.c:v0.04 10/17/93 Donald Becker (becker@super.org)\n";
+	"skeleton.c:v0.05 11/16/93 Donald Becker (becker@super.org)\n";
 
 /* Always include 'config.h' first in case the user wants to turn on
    or override something. */
@@ -49,12 +49,12 @@
 #include <linux/ioport.h>
 #include <linux/in.h>
 #include <linux/malloc.h>
+#include <linux/string.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <errno.h>
-#include <memory.h>
 
 #include "dev.h"
 #include "iow.h"
@@ -73,7 +73,12 @@
 
 #ifndef HAVE_ALLOC_SKB
 #define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff, size);
+#define kfree_skbmem(addr, size) kfree_s(addr,size);
+#endif
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 		0
+#define	snarf_region(ioaddr, size);		do ; while (0)
 #endif
 
 /* use 0 for production, 1 for verification, >2 for debug */
@@ -88,6 +93,14 @@
 	long open_time;				/* Useless example local info. */
 };
 
+/* The number of low I/O ports used by the ethercard. */
+#define ETHERCARD_TOTAL_SIZE	16
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0x42
+#define SA_ADDR2 0x65
+
 /* Index to functions, as function prototypes. */
 
 extern int netcard_probe(struct device *dev);
@@ -128,10 +141,8 @@
 
 	for (port = &ports[0]; *port; port++) {
 		int ioaddr = *port;
-#ifdef HAVE_PORTRESERVE
-		if (check_region(ioaddr, 1 /* ETHERCARD_TOTAL_SIZE */))
+		if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
 			continue;
-#endif
 		if (inb(ioaddr) != 0x57)
 			continue;
 		dev->base_addr = ioaddr;
@@ -140,7 +151,7 @@
 	}
 
 	dev->base_addr = base_addr;
-	return ENODEV;			/* ENODEV would be more accurate. */
+	return ENODEV;
 }
 
 int netcard_probe1(struct device *dev, short ioaddr)
@@ -153,7 +164,8 @@
 		station_addr[i] = inb(ioaddr + i);
 	}
 	/* Check the first three octets of the S.A. for the manufactor's code. */ 
-	if (station_addr[0] != 0x42	 ||	 station_addr[1] != 0x42 || station_addr[2] != 0x42) {
+	if (station_addr[0] != SA_ADDR0
+		||	 station_addr[1] != SA_ADDR1 || station_addr[2] != SA_ADDR2) {
 		return ENODEV;
 	}
 
@@ -188,10 +200,8 @@
 	 }
 #endif	/* jumpered interrupt */
 
-#ifdef HAVE_PORTRESERVE
 	/* Grab the region so we can find another board if autoIRQ fails. */
-	snarf_region(ioaddr, 16);
-#endif
+	snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE);
 
 	if (net_debug)
 		printk(version);
@@ -219,21 +229,21 @@
 	dev->rebuild_header	= eth_rebuild_header;
 	dev->type_trans		= eth_type_trans;
 
-	dev->type		= ARPHRD_ETHER;
+	dev->type			= ARPHRD_ETHER;
 	dev->hard_header_len = ETH_HLEN;
-	dev->mtu		= 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
+	dev->mtu			= 1500; /* eth_mtu */
+	dev->addr_len		= ETH_ALEN;
 	for (i = 0; i < ETH_ALEN; i++) {
 		dev->broadcast[i]=0xff;
 	}
 
 	/* 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	= sizeof(unsigned long);
+	dev->flags			= IFF_BROADCAST;
+	dev->family			= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr		= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= sizeof(unsigned long);
 
 	return 0;
 }
@@ -422,7 +432,7 @@
 #else
 			skb->lock = 0;
 			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_skbmem(skb, sksize);
+				kfree_s(skb, sksize);
 				lp->stats.rx_dropped++;
 				break;
 			}
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 85a14ad..0c3720f 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -14,6 +14,9 @@
  *		Alan Cox	: 	Found cause of overrun. ifconfig sl0 mtu upwards.
  *					Driver now spots this and grows/shrinks its buffers(hack!).
  *					Memory leak if you run out of memory setting up a slip driver fixed.
+ *		Matt Dillon	:	Printable slip (borrowed from NET2E)
+ *	Pauline Middelink	:	Slip driver fixes.
+ *		Alan Cox	:	Honours the old SL_COMPRESSED flag
  */
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -77,7 +80,7 @@
 
   ip = (struct iphdr *) ptr;
   th = (struct tcphdr *) (ptr + ip->ihl * 4);
-  printk("\r%s -> %s seq %x ack %x len %d\n",
+  printk("\r%s -> %s seq %lx ack %lx len %d\n",
 	 in_ntoa(ip->saddr), in_ntoa(ip->daddr), 
 	 ntohl(th->seq), ntohl(th->ack_seq), ntohs(ip->tot_len));
   return;
@@ -125,6 +128,12 @@
   sl->inuse		= 0;
   sl->sending		= 0;
   sl->escape		= 0;
+  sl->flags		= 0;
+#ifdef SL_COMPRESSED
+  sl->mode		= SL_MODE_CSLIP;	/* Default */
+#else
+  sl->mode		= SL_MODE_SLIP;		/* Default for non compressors */
+#endif  
 
   sl->line		= dev->base_addr;
   sl->tty		= NULL;
@@ -180,6 +189,7 @@
 		return(sl);
 	}
   }
+  restore_flags(flags);
   return(NULL);
 }
 
@@ -335,7 +345,7 @@
   int count;
 
   count = sl->rcount;
-  if (1) {
+  if (sl->mode & SL_MODE_CSLIP) {
     if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
       /* make sure we've reserved enough space for uncompress to use */
       save_flags(flags);
@@ -397,7 +407,6 @@
 sl_encaps(struct slip *sl, unsigned char *icp, int len)
 {
   unsigned char *bp, *p;
-  unsigned char c;
   int count;
 
   DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", icp, len));
@@ -408,17 +417,17 @@
   if(sl->mtu != sl->dev->mtu)	/* Someone has been ifconfigging */
   	sl_changedmtu(sl);
   
-  if(len>=sl->mtu)		/* Sigh, shouldn't occur BUT ... */
+  if(len>sl->mtu)		/* Sigh, shouldn't occur BUT ... */
   {
-  	len=sl->mtu-1;
+  	len=sl->mtu;
   	printk("slip: truncating oversized transmit packet!\n");
   }
 
   p = icp;
-#ifdef SL_COMPRESSED
-  len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
-#endif
-  
+  if(sl->mode & SL_MODE_CSLIP)
+	  len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+
+#ifdef OLD  
   /*
    * Send an initial END character to flush out any
    * data that may have accumulated in the receiver
@@ -450,8 +459,14 @@
 			count++;
 	}
   }
-  *bp++ = END;
+  *bp++ = END;  
   count++;
+#else
+  if(sl->mode & SL_MODE_SLIP6)
+  	count=slip_esc6(p, (unsigned char *)sl->xbuff,len);
+  else
+  	count=slip_esc(p, (unsigned char *)sl->xbuff,len);
+#endif  	  
   sl->spacket++;
   bp = sl->xbuff;
 
@@ -640,10 +655,10 @@
 slip_recv(struct tty_struct *tty)
 {
   unsigned char buff[128];
-  unsigned char *p, c;
+  unsigned char *p;
   struct slip *sl;
-  int count;
-
+  int count, error=0;
+  
   DPRINTF((DBG_SLIP, "SLIP: slip_recv(%d) called\n", tty->line));
   if ((sl = sl_find(tty)) == NULL) return;	/* not connected */
 
@@ -653,9 +668,15 @@
   /* Suck the bytes out of the TTY queues. */
   do {
 	count = tty_read_raw_data(tty, buff, 128);
-	if (count <= 0) break;
-
+	if (count <= 0)
+	{
+		count= - count;
+		if(count)
+			error=1;
+		break;
+	}
 	p = buff;
+#ifdef OLD	
 	while (count--) {
 		c = *p++;
 		if (sl->escape) {
@@ -676,7 +697,14 @@
 			} else	sl_enqueue(sl, c);
 		}
 	}
+#else
+	if(sl->mode & SL_MODE_SLIP6)
+		slip_unesc6(sl,buff,count,error);
+	else
+		slip_unesc(sl,buff,count,error);
+#endif		
   } while(1);
+  
 }
 
 
@@ -741,12 +769,181 @@
 					tty->line, sl->dev->name));
 }
 
+ 
+ /************************************************************************
+  *			STANDARD SLIP ENCAPSULATION			*
+  ************************************************************************
+  *
+  */
+ 
+ int
+ slip_esc(unsigned char *s, unsigned char *d, int len)
+ {
+     int count = 0;
+ 
+     /*
+      * Send an initial END character to flush out any
+      * data that may have accumulated in the receiver
+      * due to line noise.
+      */
+ 
+     d[count++] = END;
+ 
+     /*
+      * For each byte in the packet, send the appropriate
+      * character sequence, according to the SLIP protocol.
+      */
+ 
+     while(len-- > 0) {
+     	switch(*s) {
+     	case END:
+     	    d[count++] = ESC;
+     	    d[count++] = ESC_END;
+ 	    break;
+ 	case ESC:
+     	    d[count++] = ESC;
+     	    d[count++] = ESC_ESC;
+ 	    break;
+ 	default:
+ 	    d[count++] = *s;
+ 	}
+ 	++s;
+     }
+     d[count++] = END;
+     return(count);
+ }
+ 
+ void
+ slip_unesc(struct slip *sl, unsigned char *s, int count, int error)
+ {
+     int i;
+ 
+     for (i = 0; i < count; ++i, ++s) {
+ 	switch(*s) {
+ 	case ESC:
+ 	    sl->flags |= SLF_ESCAPE;
+ 	    break;
+ 	case ESC_ESC:
+ 	    if (sl->flags & SLF_ESCAPE)
+ 	    	sl_enqueue(sl, ESC);
+ 	    else
+ 	        sl_enqueue(sl, *s);
+	    sl->flags &= ~SLF_ESCAPE;
+ 	    break;
+	case ESC_END:
+ 	    if (sl->flags & SLF_ESCAPE)
+	    	sl_enqueue(sl, END);
+	    else
+ 	        sl_enqueue(sl, *s);
+	    sl->flags &= ~SLF_ESCAPE;
+	    break;
+	case END:
+ 	    if (sl->rcount > 2) 
+ 	    	sl_bump(sl);
+ 	    sl_dequeue(sl, sl->rcount);
+ 	    sl->rcount = 0;
+ 	    sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
+ 	    break;
+ 	default:
+ 	    sl_enqueue(sl, *s);
+ 	    sl->flags &= ~SLF_ESCAPE;
+ 	}
+     }
+     if (error)
+     	sl->flags |= SLF_ERROR;
+ }
+ 
+ /************************************************************************
+  *			 6 BIT SLIP ENCAPSULATION			*
+  ************************************************************************
+  *
+  */
+ 
+ int
+ slip_esc6(unsigned char *s, unsigned char *d, int len)
+ {
+     int count = 0;
+     int i;
+     unsigned short v = 0;
+     short bits = 0;
+ 
+     /*
+      * Send an initial END character to flush out any
+      * data that may have accumulated in the receiver
+      * due to line noise.
+      */
+ 
+     d[count++] = 0x70;
+ 
+     /*
+      * Encode the packet into printable ascii characters
+      */
+ 
+     for (i = 0; i < len; ++i) {
+     	v = (v << 8) | s[i];
+     	bits += 8;
+     	while (bits >= 6) {
+     	    unsigned char c;
+ 
+     	    bits -= 6;
+     	    c = 0x30 + ((v >> bits) & 0x3F);
+     	    d[count++] = c;
+ 	}
+     }
+     if (bits) {
+     	unsigned char c;
+ 
+     	c = 0x30 + ((v << (6 - bits)) & 0x3F);
+     	d[count++] = c;
+     }
+     d[count++] = 0x70;
+     return(count);
+ }
+ 
+ void
+ slip_unesc6(struct slip *sl, unsigned char *s, int count, int error)
+ {
+     int i;
+     unsigned char c;
+ 
+     for (i = 0; i < count; ++i, ++s) {
+     	if (*s == 0x70) {
+ 	    if (sl->rcount > 8) {	/* XXX must be 2 for compressed slip */
+ #ifdef NOTDEF
+ 	        printk("rbuff %02x %02x %02x %02x\n",
+ 	            sl->rbuff[0],
+ 	            sl->rbuff[1],
+ 	            sl->rbuff[2],
+ 	            sl->rbuff[3]
+ 	        );
+ #endif
+ 	    	sl_bump(sl);
+ 	    }
+ 	    sl_dequeue(sl, sl->rcount);
+ 	    sl->rcount = 0;
+ 	    sl->flags &= ~(SLF_ESCAPE | SLF_ERROR); /* SLF_ESCAPE not used */
+ 	    sl->xbits = 0;
+ 	} else if (*s >= 0x30 && *s < 0x70) {
+ 	    sl->xdata = (sl->xdata << 6) | ((*s - 0x30) & 0x3F);
+ 	    sl->xbits += 6;
+ 	    if (sl->xbits >= 8) {
+ 	    	sl->xbits -= 8;
+ 	    	c = (unsigned char)(sl->xdata >> sl->xbits);
+ 		sl_enqueue(sl, c);
+ 	    }
+ 
+ 	}
+     }
+     if (error)
+     	sl->flags |= SLF_ERROR;
+ }
 
 /* Perform I/O control on an active SLIP channel. */
 static int
 slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
 {
   struct slip *sl;
+  int err;
 
   /* First make sure we're connected. */
   if ((sl = sl_find(tty)) == NULL) {
@@ -757,9 +954,19 @@
   DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
   switch(cmd) {
 	case SIOCGIFNAME:
-		verify_area(VERIFY_WRITE, arg, 16);
+		err=verify_area(VERIFY_WRITE, arg, 16);
+		if(err)
+			return -err;
 		memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
 		return(0);
+	case SIOCGIFENCAP:
+		err=verify_area(VERIFY_WRITE,arg,sizeof(long));
+		put_fs_long(sl->mode,(long *)arg);
+		return(0);
+	case SIOCSIFENCAP:
+		err=verify_area(VERIFY_READ,arg,sizeof(long));
+		sl->mode=get_fs_long((long *)arg);
+		return(0);
 	default:
 		return(-EINVAL);
   }
diff --git a/drivers/net/slip.h b/drivers/net/slip.h
index cd81d60..6c779ab 100644
--- a/drivers/net/slip.h
+++ b/drivers/net/slip.h
@@ -8,6 +8,7 @@
  *
  * Fixes:
  *		Alan Cox	: 	Added slip mtu field.
+ *		Matt Dillon	:	Printable slip (borrowed from net2e)
  *
  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  */
@@ -56,10 +57,25 @@
   unsigned long		errors;		/* error count			*/
   
   int			mtu;		/* Our mtu (to spot changes!)   */
+  unsigned char		flags;		/* Flag values/ mode etc	*/
+#define SLF_ESCAPE	2
+#define SLF_ERROR	4
+#define SLF_COMP	16
+#define SLF_EXPN	32
+  unsigned char		mode;		/* SLIP mode			*/
+#define SL_MODE_SLIP	0
+#define SL_MODE_CSLIP	1
+#define SL_MODE_SLIP6	2		/* Matt Dillon's printable slip */
+#define SL_MODE_CSLIP6	(SL_MODE_SLIP|SL_MODE_CSLIP)
+  int			xdata,xbits;	/* 6 bit slip controls 		*/
 };
 
 
 extern int	slip_init(struct device *dev);
+extern int	slip_esc(unsigned char *s, unsigned char *d, int len);
+extern int	slip_esc6(unsigned char *s, unsigned char *d, int len);
+extern void	slip_unesc(struct slip *sl, unsigned char *s, int count, int error);
+extern void 	slip_unesc6(struct slip *sl, unsigned char *s, int count, int error);
 
 
 #endif	/* _LINUX_SLIP.H */
diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
index 2947f96..726e1c8 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/smc-ultra.c
@@ -14,19 +14,25 @@
 */
 
 static char *version =
-    "smc-ultra.c:v0.02 10/8/93 Donald Becker (becker@super.org)\n";
+    "smc-ultra.c:v0.03 11/21/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 #include <asm/io.h>
 #include <asm/system.h>
-#include <memory.h>
 
 #include "dev.h"
 #include "8390.h"
 
+/* Compatibility definitions for earlier kernel versions. */
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size)              0
+#define snarf_region(ioaddr, size);             do ; while (0)
+#endif
+
 int ultraprobe(int ioaddr, struct device *dev);
 int ultraprobe1(int ioaddr, struct device *dev);
 
@@ -53,19 +59,17 @@
 
 int ultra_probe(struct device *dev)
 {
-    int *port, ports[] = {0x300, 0x280, 0x380, 0x240, 0};
-    short ioaddr = dev->base_addr;
+    int *port, ports[] = {0x200, 0x220, 0x240, 0x280, 0x300, 0x380, 0};
+    unsigned short ioaddr = dev->base_addr;
 
-    if (ioaddr < 0)
-	return ENXIO;		/* Don't probe at all. */
-    if (ioaddr > 0x100)
+    if (ioaddr > 0x1ff)
 	return ! ultraprobe1(ioaddr, dev);
+    else if (ioaddr > 0)
+	return ENXIO;		/* Don't probe at all. */
 
     for (port = &ports[0]; *port; port++) {
-#ifdef HAVE_PORTRESERVE
 	if (check_region(*port, 32))
 	    continue;
-#endif
 	if ((inb(*port + 7) & 0xF0) == 0x20
 	    && ultraprobe1(*port, dev) == 0)
 	    return 0;
@@ -81,7 +85,14 @@
   int checksum = 0;
   char *model_name;
   int num_pages;
+  unsigned char reg1, eeprom_irq = 0;
 
+  /* Second probe check: at most one bit can be set in register 1. */
+  reg1 = inb(ioaddr + 1);
+  if (reg1 & (reg1 - 1))
+      return ENODEV;
+
+  /* Select the station address register set. */
   outb(0x7f & inb(ioaddr + 4), ioaddr + 4);
 
   for (i = 0; i < 8; i++)
@@ -93,37 +104,30 @@
   for (i = 0; i < 6; i++)
       printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
 
+  /* Switch from the station address to the alternate register set. */
   outb(0x80 | inb(ioaddr + 4), ioaddr + 4);
 
   model_name = "SMC Ultra";
 
   if (dev->irq < 2) {
+      unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+      int irqreg = inb(ioaddr + 0xd);
       int irq;
-      /* Datasheet doesn't specify the IRQ line mapping -> always autoIRQ. */
 
-      outb(0x05, ioaddr + 6);
-      autoirq_setup(0);
+      /* The IRQ bits are split. */
+      irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
 
-      /* Trigger an interrupt, then release. */
-      outb_p(0x09, ioaddr + 6);
-      outb(0x00, ioaddr + 6);
-
-      irq = autoirq_report(1);
-      if (irq)
-	  printk(", using IRQ %d", irq);
-      else {
+      if (irq == 0) {
 	  printk(", failed to detect IRQ line.\n");
 	  return -EAGAIN;
       }
       dev->irq = irq;
-  } else
-      printk(" assigned IRQ %d.\n", dev->irq);
+      eeprom_irq = 1;
+  }
 
 
   /* OK, were are certain this is going to work.  Setup the device. */
-#ifdef HAVE_PORTRESERVE
   snarf_region(ioaddr, 32);
-#endif
 
   /* The 8390 isn't at the base address, so fake the offset */
   dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
@@ -149,7 +153,8 @@
   dev->mem_end = dev->rmem_end
       = dev->mem_start + (ei_status.stop_page - START_PG)*256;
 
-  printk(", shared memory at %#x-%#x.\n", dev->mem_start, dev->mem_end-1);
+  printk(",%s IRQ %d memory %#x-%#x.\n", eeprom_irq ? "" : "assigned ",
+	 dev->irq, dev->mem_start, dev->mem_end-1);
   if (ei_debug > 0)
       printk(version);
 
diff --git a/drivers/net/wd.c b/drivers/net/wd.c
index ea1244e..f88ec98 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/wd.c
@@ -1,236 +1,254 @@
 /* wd.c: A WD80x3 ethernet driver for linux. */
 /*
-    Written 1993 by Donald Becker.
-    Copyright 1993 United States Government as represented by the
-    Director, National Security Agency.  This software may be used and
-    distributed according to the terms of the GNU Public License,
-    incorporated herein by reference.
-    
-    This is a driver for WD8003 and WD8013 "compatible" ethercards.
+	Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the
+	Director, National Security Agency.	 This software may be used and
+	distributed according to the terms of the GNU Public License,
+	incorporated herein by reference.
+	
+	This is a driver for WD8003 and WD8013 "compatible" ethercards.
 
-    The Author may be reached as becker@super.org or
-    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+	The Author may be reached as becker@super.org or
+	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
 
-    Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
 */
 
 static char *version =
-    "wd.c:v0.99-14 10/13/93 Donald Becker (becker@super.org)\n";
+	"wd.c:v0.99-14 11/21/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 #include <asm/io.h>
 #include <asm/system.h>
-#include <memory.h>
 
 #include "dev.h"
 #include "8390.h"
 
-int wdprobe(int ioaddr, struct device *dev);
+/* Compatibility definitions for earlier kernel versions. */
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size)              0
+#define snarf_region(ioaddr, size);             do ; while (0)
+#endif
+
+int wd_probe(struct device *dev);
 int wdprobe1(int ioaddr, struct device *dev);
 
 static int wd_open(struct device *dev);
 static void wd_reset_8390(struct device *dev);
 static int wd_block_input(struct device *dev, int count,
-			  char *buf, int ring_offset);
+						  char *buf, int ring_offset);
 static void wd_block_output(struct device *dev, int count,
-			    const unsigned char *buf, const start_page);
+							const unsigned char *buf, const start_page);
 static int wd_close_card(struct device *dev);
 
 
-#define WD_START_PG	0x00	/* First page of TX buffer */
+#define WD_START_PG		0x00	/* First page of TX buffer */
 #define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
 #define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
 
-#define WD_CMDREG	0	/* Offset to ASIC command register. */
-#define  WD_RESET	0x80	/* Board reset, in WD_CMDREG. */
-#define  WD_MEMENB	0x40	/* Enable the shared memory. */
-#define WD_CMDREG5	5	/* Offset to 16-bit-only ASIC register 5. */
-#define  ISA16		0x80	/* Enable 16 bit access from the ISA bus. */
-#define  NIC16		0x40	/* Enable 16 bit access from the 8390. */
-#define WD_NIC_OFFSET	16	/* Offset to the 8390 NIC from the base_addr. */
+#define WD_CMDREG		0		/* Offset to ASIC command register. */
+#define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */
+#define	 WD_MEMENB		0x40	/* Enable the shared memory. */
+#define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */
+#define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */
+#define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET	16		/* Offset to the 8390 NIC from the base_addr. */
 
-/*  Probe for the WD8003 and WD8013.  These cards have the station
-    address PROM at I/O ports <base>+8 to <base>+13, with a checksum
-    following. A Soundblaster can have the same checksum as an WDethercard,
-    so we have an extra exclusionary check for it.
+/*	Probe for the WD8003 and WD8013.  These cards have the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following. A Soundblaster can have the same checksum as an WDethercard,
+	so we have an extra exclusionary check for it.
 
-    The wdprobe1() routine initializes the card and fills the
-    station address field. */
+	The wdprobe1() routine initializes the card and fills the
+	station address field. */
 
 int wd_probe(struct device *dev)
 {
-    int *port, ports[] = {0x300, 0x280, 0x380, 0x240, 0};
-    short ioaddr = dev->base_addr;
+	int *port, ports[] = {0x300, 0x280, 0x380, 0x240, 0};
+	short ioaddr = dev->base_addr;
 
-    if (ioaddr < 0)
-	return ENXIO;		/* Don't probe at all. */
-    if (ioaddr > 0x100)
-	return ! wdprobe1(ioaddr, dev);
+	if (ioaddr < 0)
+		return ENXIO;			/* Don't probe at all. */
+	if (ioaddr > 0x100)
+		return ! wdprobe1(ioaddr, dev);
 
-    for (port = &ports[0]; *port; port++) {
-#ifdef HAVE_PORTRESERVE
-	if (check_region(*port, 32))
-	    continue;
-#endif
-	if (inb(*port + 8) != 0xff
-	    && inb(*port + 9) != 0xff /* Extra check to avoid soundcard. */
-	    && wdprobe1(*port, dev))
-	    return 0;
-    }
-    dev->base_addr = ioaddr;
-    return ENODEV;
+	for (port = &ports[0]; *port; port++) {
+		if (check_region(*port, 32))
+			continue;
+		if (inb(*port + 8) != 0xff
+			&& inb(*port + 9) != 0xff /* Extra check to avoid soundcard. */
+			&& wdprobe1(*port, dev))
+			return 0;
+	}
+	dev->base_addr = ioaddr;
+	return ENODEV;
 }
 
 int wdprobe1(int ioaddr, struct device *dev)
 {
-  int i;
-  unsigned char *station_addr = dev->dev_addr;
-  int checksum = 0;
-  int ancient = 0;		/* An old card without config registers. */
-  int word16 = 0;		/* 0 = 8 bit, 1 = 16 bit */
-  char *model_name;
-
-  for (i = 0; i < 8; i++)
-      checksum += inb(ioaddr + 8 + i);
-  if ((checksum & 0xff) != 0xFF)
-      return 0;
-  
-  printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
-  for (i = 0; i < 6; i++)
-      printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
-
-  /* The following PureData probe code was contributed by
-     Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
-     configuration differently from others so we have to check for them.
-     This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
-     */
-  if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
-      unsigned char reg5 = inb(ioaddr+5);
-
-      switch (inb(ioaddr+2)) {
-      case 0x03: word16 = 0; model_name = "PDI8023-8";  break;
-      case 0x05: word16 = 0; model_name = "PDUC8023";   break;
-      case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
-	  /* Either 0x01 (dumb) or they've released a new version. */
-      default:   word16 = 0; model_name = "PDI8023";    break;
-      }
-      dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
-      dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
-  } else {				/* End of PureData probe */
-      /* This method of checking for a 16-bit board is borrowed from the
-	 we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
-	 I'm comparing the two method in alpha test to make certain they
-	 return the same result. */
-      /* Check for the old 8 bit board - it has register 0/8 aliasing.
-	 Do NOT check i>=6 here -- it hangs the old 8003 boards! */
-      for (i = 0; i < 6; i++)
-	  if (inb(ioaddr+i) != inb(ioaddr+8+i))
-	      break;
-      if (i >= 6) {
-	  ancient = 1;
-	  model_name = "WD8003-old";
-	  word16 = 0;
-      } else {
-	  int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
-	  outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
-	  if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
-	      && (tmp & 0x01) == 0x01   ) {		/* In a 16 slot. */
-	      int asic_reg5 = inb(ioaddr+WD_CMDREG5);
-	      /* Magic to set ASIC to word-wide mode. */
-	      outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
-	      outb(tmp, ioaddr+1);
-	      model_name = "WD8013";
-	      word16 = 1; 	/* We have a 16bit board here! */
-	  } else {
-	      model_name = "WD8003";
-	      word16 = 0;
-	  }
-	  outb(tmp, ioaddr+1);		/* Restore original reg1 value. */
-      }
+	int i;
+	unsigned char *station_addr = dev->dev_addr;
+	int checksum = 0;
+	int ancient = 0;			/* An old card without config registers. */
+	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */
+	char *model_name;
+	
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xFF)
+		return 0;
+	
+	printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
+	
+	/* The following PureData probe code was contributed by
+	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+	   configuration differently from others so we have to check for them.
+	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+	   */
+	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+		unsigned char reg5 = inb(ioaddr+5);
+		
+		switch (inb(ioaddr+2)) {
+		case 0x03: word16 = 0; model_name = "PDI8023-8";	break;
+		case 0x05: word16 = 0; model_name = "PDUC8023";	break;
+		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+			/* Either 0x01 (dumb) or they've released a new version. */
+		default:	 word16 = 0; model_name = "PDI8023";	break;
+		}
+		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+	} else {								/* End of PureData probe */
+		/* This method of checking for a 16-bit board is borrowed from the
+		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
+		   I'm comparing the two method in alpha test to make certain they
+		   return the same result. */
+		/* Check for the old 8 bit board - it has register 0/8 aliasing.
+		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+		for (i = 0; i < 6; i++)
+			if (inb(ioaddr+i) != inb(ioaddr+8+i))
+				break;
+		if (i >= 6) {
+			ancient = 1;
+			model_name = "WD8003-old";
+			word16 = 0;
+		} else {
+			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */
+				int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+				/* Magic to set ASIC to word-wide mode. */
+				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+				outb(tmp, ioaddr+1);
+				model_name = "WD8013";
+				word16 = 1;		/* We have a 16bit board here! */
+			} else {
+				model_name = "WD8003";
+				word16 = 0;
+			}
+			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */
+		}
 #ifndef final_version
-      if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
-	  printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
-		 word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+			printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+				   word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
 #endif
-  }
-
+	}
+	
 #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
-  /* Allow a compile-time override.  */
-  dev->mem_start = WD_SHMEM;
+	/* Allow a compile-time override.	 */
+	dev->mem_start = WD_SHMEM;
 #else
-  if (dev->mem_start == 0) {
-      /* Sanity and old 8003 check */
-      int reg0 = inb(ioaddr);
-      if (reg0 == 0xff || reg0 == 0) {
-	  /* Future plan: this could check a few likely locations first. */
-	  dev->mem_start = 0xd0000;
-	  printk(" assigning address %#x", dev->mem_start);
-      } else {
-	  int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
-	  /* Some boards don't have the register 5 -- it returns 0xff. */
-	  if (high_addr_bits == 0x1f || word16 == 0)
-	      high_addr_bits = 0x01;
-	  dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
-      }
-  }
+	if (dev->mem_start == 0) {
+		/* Sanity and old 8003 check */
+		int reg0 = inb(ioaddr);
+		if (reg0 == 0xff || reg0 == 0) {
+			/* Future plan: this could check a few likely locations first. */
+			dev->mem_start = 0xd0000;
+			printk(" assigning address %#lx", dev->mem_start);
+		} else {
+			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+			/* Some boards don't have the register 5 -- it returns 0xff. */
+			if (high_addr_bits == 0x1f || word16 == 0)
+				high_addr_bits = 0x01;
+			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+		}
+	}
 #endif
-
-  /* The 8390 isn't at the base address -- the ASIC regs are there! */
-  dev->base_addr = ioaddr+WD_NIC_OFFSET;
-
-  if (dev->irq < 2) {
-      int irqmap[] = {9,3,5,7,10,11,15,4};
-      int reg1 = inb(ioaddr+1);
-      int reg4 = inb(ioaddr+4);
-      if (ancient || reg1 == 0xff)	/* Ack!! No way to read the IRQ! */
-	  dev->irq = word16 ? 10 : 5;
-      else
-	  dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
-  } else if (dev->irq == 2)	/* Fixup bogosity: IRQ2 is really IRQ9 */
-      dev->irq = 9;
-
-  /* Snarf the interrupt now.  There's no point in waiting since we cannot
-     share and the board will usually be enabled. */
-  { int irqval = irqaction (dev->irq, &ei_sigaction);
-    if (irqval) {
-	printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
-	return 0;
-    }
-  }
-
-  /* OK, were are certain this is going to work.  Setup the device. */
-#ifdef HAVE_PORTRESERVE
-  snarf_region(ioaddr, 32);
-#endif
-  ethdev_init(dev);
-
-  ei_status.name = model_name;
-  ei_status.word16 = word16;
-  ei_status.tx_start_page = WD_START_PG;
-  ei_status.rx_start_page = WD_START_PG + TX_PAGES;
-  ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
-
-  /* Don't map in the shared memory until the board is actually opened. */
-  dev->rmem_start = dev->mem_start + TX_PAGES*256;
-  dev->mem_end = dev->rmem_end
-      = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
-
-  printk(" %s, IRQ %d, shared memory at %#x-%#x.\n",
-	 model_name, dev->irq, dev->mem_start, dev->mem_end-1);
-  if (ei_debug > 0)
-      printk(version);
-
-  ei_status.reset_8390 = &wd_reset_8390;
-  ei_status.block_input = &wd_block_input;
-  ei_status.block_output = &wd_block_output;
-  dev->open = &wd_open;
-  dev->stop = &wd_close_card;
-  NS8390_init(dev, 0);
-
-  return dev->base_addr;
+	
+	/* The 8390 isn't at the base address -- the ASIC regs are there! */
+	dev->base_addr = ioaddr+WD_NIC_OFFSET;
+	
+	if (dev->irq < 2) {
+		int irqmap[] = {9,3,5,7,10,11,15,4};
+		int reg1 = inb(ioaddr+1);
+		int reg4 = inb(ioaddr+4);
+		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */
+			short nic_addr = ioaddr+WD_NIC_OFFSET;
+			
+			/* We have an old-style ethercard that doesn't report its IRQ
+			   line.  Do autoirq to find the IRQ line. Note that this IS NOT
+			   a reliable way to trigger an interrupt. */
+			outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */
+			autoirq_setup(0);
+			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */
+			outb_p(0x00, nic_addr + EN0_RCNTLO);
+			outb_p(0x00, nic_addr + EN0_RCNTHI);
+			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+			dev->irq = autoirq_report(2);
+			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */
+			
+			if (ei_debug > 2)
+				printk(" autoirq is %d", dev->irq);
+			if (dev->irq < 2)
+				dev->irq = word16 ? 10 : 5;
+		} else
+			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */
+		dev->irq = 9;
+	
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share and the board will usually be enabled. */
+	if (irqaction (dev->irq, &ei_sigaction)) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		return 0;
+	}
+	
+	/* OK, were are certain this is going to work.  Setup the device. */
+	snarf_region(ioaddr, 32);
+	ethdev_init(dev);
+	
+	ei_status.name = model_name;
+	ei_status.word16 = word16;
+	ei_status.tx_start_page = WD_START_PG;
+	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+	ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+	
+	/* Don't map in the shared memory until the board is actually opened. */
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = dev->rmem_end
+		= dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+	
+	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+	if (ei_debug > 0)
+		printk(version);
+	
+	ei_status.reset_8390 = &wd_reset_8390;
+	ei_status.block_input = &wd_block_input;
+	ei_status.block_output = &wd_block_output;
+	dev->open = &wd_open;
+	dev->stop = &wd_close_card;
+	NS8390_init(dev, 0);
+	
+	return dev->base_addr;
 }
 
 static int
@@ -239,12 +257,12 @@
   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 
   /* Map in the shared memory. Always set register 0 last to remain
-     compatible with very old boards. */
+	 compatible with very old boards. */
   ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
   ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
 
   if (ei_status.word16)
-      outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+	  outb(ei_status.reg5, ioaddr+WD_CMDREG5);
   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
 
   return ei_open(dev);
@@ -253,19 +271,19 @@
 static void
 wd_reset_8390(struct device *dev)
 {
-    int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 
-    outb(WD_RESET, wd_cmd_port);
-    if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
-    ei_status.txing = 0;
+	outb(WD_RESET, wd_cmd_port);
+	if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
+	ei_status.txing = 0;
 
-    /* Set up the ASIC registers, just in case something changed them. */
-    outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
-    if (ei_status.word16)
-	outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+	/* Set up the ASIC registers, just in case something changed them. */
+	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+	if (ei_status.word16)
+		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
 
-    if (ei_debug > 1) printk("reset done\n");
-    return;
+	if (ei_debug > 1) printk("reset done\n");
+	return;
 }
 
 /* Block input and output are easy on shared memory ethercards, and trivial
@@ -276,76 +294,76 @@
 static int
 wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
 {
-    int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
-    void *xfer_start = (void *)(dev->mem_start + ring_offset
-				- (WD_START_PG<<8));
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
 
-    /* We'll always get a 4 byte header read first. */
-    if (count == 4) {
+	/* We'll always get a 4 byte header read followed by a packet read, so
+	   we enable 16 bit mode before the header, and disable after the body. */
+	if (count == 4) {
+		if (ei_status.word16)
+			outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+		((int*)buf)[0] = ((int*)xfer_start)[0];
+		return 0;
+	}
+
+	if (xfer_start + count > dev->rmem_end) {
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy(buf, (char *)xfer_start, semi_count);
+		count -= semi_count;
+		memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+	} else
+		memcpy(buf, (char *)xfer_start, count);
+
+	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */
 	if (ei_status.word16)
-	    outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
-	((int*)buf)[0] = ((int*)xfer_start)[0];
+		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
 	return 0;
-    }
-
-    if (xfer_start + count > (void*) dev->rmem_end) {
-	/* We must wrap the input move. */
-	int semi_count = (void*)dev->rmem_end - xfer_start;
-	memcpy(buf, xfer_start, semi_count);
-	count -= semi_count;
-	memcpy(buf + semi_count, (char *)dev->rmem_start, count);
-    } else
-	memcpy(buf, xfer_start, count);
-
-    /* Turn off 16 bit access so that reboot works.  ISA brain-damage */
-    if (ei_status.word16)
-	outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
-
-    return 0;
 }
 
 static void
 wd_block_output(struct device *dev, int count, const unsigned char *buf,
-		int start_page)
+				int start_page)
 {
-    int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
-    unsigned char *shmem
-	= (unsigned char *)dev->mem_start + ((start_page - WD_START_PG)<<8);
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
 
 
-    if (ei_status.word16) {
-	/* Turn on and off 16 bit access so that reboot works. */
-	outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
-	memcpy(shmem, buf, count);
-	outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
-    } else
-	memcpy(shmem, buf, count);
+	if (ei_status.word16) {
+		/* Turn on and off 16 bit access so that reboot works. */
+		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+		memcpy((char *)shmem, buf, count);
+		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+	} else
+		memcpy((char *)shmem, buf, count);
 }
 
 
 static int
 wd_close_card(struct device *dev)
 {
-    int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 
-    if (ei_debug > 1)
-	printk("%s: Shutting down ethercard.\n", dev->name);
-    NS8390_init(dev, 0);
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+	NS8390_init(dev, 0);
 
-    /* Change from 16-bit to 8-bit shared memory so reboot works. */
-    outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+	/* Change from 16-bit to 8-bit shared memory so reboot works. */
+	outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
 
-    /* And disable the shared memory. */
-    outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+	/* And disable the shared memory. */
+	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
 
-    return 0;
+	return 0;
 }
 
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/tcp -c wd.c"
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
  *  version-control: t
+ *  tab-width: 4
  *  kept-new-versions: 5
  * End:
  */
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 2144533..76e3dc2 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -128,10 +128,6 @@
 endif
 
 
-clean:
-	rm -f core *.o *.a *.s
-
-
 #
 # include a dependency file if one exists
 #
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index caa0c5a..8b27142 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -2117,29 +2117,28 @@
 
 
 /* 
- * Function : int NCR5380_reset (void)
+ * Function : int NCR5380_reset (struct Scsi_Cmnd *)
  * 
  * Purpose : reset the SCSI bus.
  *
  * Returns : 0
- *
- * XXX we really need to add some sort of a per instance field here so that 
- * we only do a SCSI bus reset on the host adapter for which the reset command
- * was called.
  */ 
 
 #ifndef NCR5380_reset
 static
 #endif
-int NCR5380_reset (void) {
+int NCR5380_reset (Scsi_Cmnd * SCpnt) {
     NCR5380_local_declare();
     struct Scsi_Host *instance;
     cli();
-    for (instance = first_instance; instance; instance = instance->next) {
-	NCR5380_setup(instance);
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
-	udelay(1);
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-    }
+
+    instance = SCpnt->host;
+    NCR5380_setup(instance);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+    udelay(1);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    sti();
+    if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
     return 0;
 }
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
index c230a50..bedf225 100644
--- a/drivers/scsi/NCR5380.h
+++ b/drivers/scsi/NCR5380.h
@@ -236,7 +236,7 @@
 #ifndef NCR5380_reset
 static
 #endif
-int NCR5380_reset (void);
+int NCR5380_reset (Scsi_Cmnd *);
 #ifndef NCR5380_queue_command
 static 
 #endif
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index eedefdb..1f90af8 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -20,10 +20,17 @@
  * General Public License for more details.
  
  *
- * $Id: aha152x.c,v 0.97 1993/10/09 18:53:53 root Exp $
+ * $Id: aha152x.c,v 0.99 1993/10/24 16:19:59 root Exp root $
  *
 
  * $Log: aha152x.c,v $
+ * Revision 0.99  1993/10/24  16:19:59  root
+ * - fixed DATA IN (rare read errors gone)
+ *
+ * Revision 0.98  1993/10/17  12:54:44  root
+ * - fixed some recent fixes (shame on me)
+ * - moved initialization of scratch area to aha152x_queue
+ *
  * Revision 0.97  1993/10/09  18:53:53  root
  * - DATA IN fixed. Rarely left data in the fifo.
  *
@@ -204,13 +211,16 @@
 
 #endif
 
-#define DEBUG_BIOSPARAM         /* warn when biosparam is invoked */
 #define DEBUG_RESET             /* resets should be rare */
 #define DEBUG_ABORT             /* aborts too */
 
 /* END OF DEFINES */
 
-char *aha152x_id = "Adaptec 152x SCSI driver; $Revision: 0.97 $\n";
+/* some additional "phases" for getphase() */
+#define P_BUSFREE  1
+#define P_PARITY   2
+
+char *aha152x_id = "Adaptec 152x SCSI driver; $Revision: 0.99 $\n";
 
 static int port_base      = 0;
 static int this_host      = 0;
@@ -401,9 +411,8 @@
  * return value is a valid phase or an error code.
  *
  * errorcodes:
- *   1 BUS FREE phase detected
- *   2 RESET IN detected
- *   3 parity error in DATA phase
+ *   P_BUSFREE   BUS FREE phase detected
+ *   P_PARITY    parity error in DATA phase
  */
 static int getphase(void)
 {
@@ -416,7 +425,7 @@
           while( !( ( sstat1 = GETPORT( SSTAT1 ) ) & (BUSFREE|SCSIRSTI|REQINIT ) ) )
             ;
           if( sstat1 & BUSFREE )
-            return 1;
+            return P_BUSFREE;
           if( sstat1 & SCSIRSTI )
             {
               /* IBM drive responds with RSTI to RSTO */
@@ -433,18 +442,12 @@
       if( TESTHI( SSTAT1, SCSIPERR ) )
         {
           if( (phase & (CDO|MSGO))==0 )                         /* DATA phase */
-            {
-              return 3;
-            }
+            return P_PARITY;
 
-          SETPORT( SCSISIG, phase );
           make_acklow();
         }
       else
-        {
-          SETPORT( SCSISIG, phase );
-          return phase;
-        }
+        return phase;
     }
 }
 
@@ -660,7 +663,7 @@
   do_pause(5);
   CLRBITS(SCSISEQ, SCSIRSTO );
 
-  aha152x_reset();
+  aha152x_reset(NULL);
 
   printk("aha152x: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d, reconnect=%s, parity=enabled\n",
          port_base, interrupt_level, this_host, can_disconnect ? "enabled" : "disabled" );
@@ -719,15 +722,41 @@
   disp_ports();
 #endif
 
+  SCpnt->scsi_done =       done;
+
+  /* setup scratch area
+     SCp.ptr              : buffer pointer
+     SCp.this_residual    : buffer length
+     SCp.buffer           : next buffer
+     SCp.buffers_residual : left buffers in list
+     SCp.phase            : current state of the command */
+  SCpnt->SCp.phase = not_issued;
+  if (SCpnt->use_sg)
+    {
+      SCpnt->SCp.buffer           = (struct scatterlist *)SCpnt->request_buffer;
+      SCpnt->SCp.ptr              = SCpnt->SCp.buffer->address;
+      SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
+      SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+    }
+  else
+    {
+      SCpnt->SCp.ptr              = (char *)SCpnt->request_buffer;
+      SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
+      SCpnt->SCp.buffer           = NULL;
+      SCpnt->SCp.buffers_residual = 0;
+    }
+          
+  SCpnt->SCp.Status              = CHECK_CONDITION;
+  SCpnt->SCp.Message             = 0;
+  SCpnt->SCp.have_data_in        = 0;
+  SCpnt->SCp.sent_command        = 0;
+
   /* Turn led on, when this is the first command. */
   cli();
   commands++;
   if(commands==1)
     SETPORT( PORTA, 1 );
 
-  SCpnt->scsi_done =       done;
-  SCpnt->SCp.phase = not_issued;
-
 #if defined(DEBUG_QUEUES)
   printk("i+ (%d), ", commands );
 #endif
@@ -868,7 +897,7 @@
 
   SETPORT(BRSTCNTRL, 0xf1);
 
-  /* clear channel 0 and transfer count */
+  /* clear SCSI fifo and transfer count */
   SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
   SETPORT(SXFRCTL0, CH1);
 
@@ -881,7 +910,7 @@
  *  Reset registers, reset a hanging bus and
  *  kill active and disconnected commands
  */
-int aha152x_reset(void)
+int aha152x_reset(Scsi_Cmnd * __unused)
 {
   Scsi_Cmnd *ptr;
 
@@ -1111,7 +1140,7 @@
 
       SETPORT( SXFRCTL0, CH1);
 
-      identify_msg = GETPORT(SCSIBUS);
+      identify_msg = GETPORT(SCSIDAT);
 
       if(!(identify_msg & IDENTIFY_BASE))
         {
@@ -1123,8 +1152,6 @@
       make_acklow();
       getphase();
 
-      SETPORT(SCSISIG, P_MSGI);
-
 #if defined(DEBUG_QUEUES)
       printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f );
 #endif
@@ -1169,35 +1196,9 @@
           sti();
 
 #if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
-          printk("issuing command, ");
+          printk("issueing command, ");
 #endif
-
-          /* setup SCSI pointers
-             SCp.ptr              : buffer pointer
-             SCp.this_residual    : buffer length
-             SCp.buffer           : next buffer
-             SCp.buffers_residual : left buffers in list */
-          if (current_SC->use_sg)
-            {
-              current_SC->SCp.buffer =
-                (struct scatterlist *)current_SC->request_buffer;
-              current_SC->SCp.ptr              = current_SC->SCp.buffer->address;
-              current_SC->SCp.this_residual    = current_SC->SCp.buffer->length;
-              current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
-            }
-          else
-            {
-              current_SC->SCp.ptr              = (char *)current_SC->request_buffer;
-              current_SC->SCp.this_residual    = current_SC->request_bufflen;
-              current_SC->SCp.buffer           = NULL;
-              current_SC->SCp.buffers_residual = 0;
-            }
-          
-          current_SC->SCp.Status              = CHECK_CONDITION;
-          current_SC->SCp.Message             = 0;
-          current_SC->SCp.have_data_in        = 0;
-          current_SC->SCp.sent_command        = 0;
-          current_SC->SCp.phase               = in_selection;
+          current_SC->SCp.phase = in_selection;
 
   #if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
           printk("selecting %d, ", current_SC->target); 
@@ -1331,6 +1332,8 @@
 
   /* enable interrupt, when target leaves current phase */
   phase = getphase();
+  if(!(phase & ~P_MASK))                                      /* "real" phase */
+    SETPORT(SCSISIG, phase);
   SETPORT(SSTAT1, CLRPHASECHG);
   current_SC->SCp.phase =
     (current_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16 );
@@ -1355,7 +1358,8 @@
           }
         else
           /* If we didn't identify yet, do it. Otherwise there's nothing to do,
-             but reject (perhaps one could do as NOP as well) */
+             but reject (probably we got an message before, that we have to
+             reject (SDTR, WDTR, etc.) */
           if( !(current_SC->SCp.phase & sent_ident))
             {
               message=IDENTIFY(can_disconnect,current_SC->lun);
@@ -1372,23 +1376,25 @@
 #endif
             }
           
-        CLRSETBITS( SXFRCTL0, ENDMA, SPIOEN);
+        CLRBITS( SXFRCTL0, ENDMA);
 
-        SETPORT( SIMODE0, ENSPIORDY );
-        SETPORT( SIMODE1, ENPHASEMIS );
+        SETPORT( SIMODE0, 0 );
+        SETPORT( SIMODE1, ENPHASEMIS|ENREQINIT );
 
         /* wait for data latch to become ready or a phase change */
         while( TESTLO( DMASTAT, INTSTAT ) )
           ;
 
-        if( TESTLO( SSTAT0, SPIORDY ) )
-          aha152x_panic("couldn't send message");
+        if( TESTHI( SSTAT1, PHASEMIS ) )
+          aha152x_panic("unable to send message");
 
         /* Leave MESSAGE OUT after transfer */
         SETPORT( SSTAT1, CLRATNO);
 
         SETPORT( SCSIDAT, message );
-        CLRBITS( SXFRCTL0, SPIOEN);
+
+        make_acklow();
+        getphase();
 
         if(message==IDENTIFY(can_disconnect,current_SC->lun))
           current_SC->SCp.phase |= sent_ident;
@@ -1418,27 +1424,19 @@
 #endif
       if( !(current_SC->SCp.sent_command) )
         {
-          if(GETPORT(FIFOSTAT))
-            {
-              int i;
+          if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+            printk("aha152x: P_CMD: %d(%d) bytes left in FIFO, resetting\n",
+                   GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
 
-              printk("aha152x: %d bytes left in FIFO, resetting\n",
-                     GETPORT(FIFOSTAT));
-              disp_ports();
-              printk("contents ( ");
-              SETPORT( SXFRCTL0, CH1|SPIOEN );
-              for(i=0; i<GETPORT(FIFOSTAT); i++)
-                printk("%02x ", GETPORT(SCSIDAT));
-              SETPORT( SXFRCTL0, CH1 );
-              printk(")\n");
-            }
-
+          /* reset fifo and enable writes */
           SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
           SETPORT(DMACNTRL0, ENDMA|WRITE_READ);
 
+          /* clear transfer count and scsi fifo */
           SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 );
           SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
-   
+  
+          /* missing phase raises INTSTAT */
           SETPORT( SIMODE0, 0 );
           SETPORT( SIMODE1, ENPHASEMIS );
   
@@ -1497,10 +1495,10 @@
 
       SETPORT( SIMODE0, 0);
       SETPORT( SIMODE1, ENBUSFREE);
-
+  
       while( phase == P_MSGI ) 
         {
-          current_SC->SCp.Message = GETPORT( SCSIBUS );
+          current_SC->SCp.Message = GETPORT( SCSIDAT );
           switch(current_SC->SCp.Message)
             {
             case DISCONNECT:
@@ -1512,7 +1510,7 @@
               if(!can_disconnect)
                 aha152x_panic("target was not allowed to disconnect");
               break;
-            
+        
             case COMMAND_COMPLETE:
 #if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
               printk("inbound message ( COMMAND COMPLETE ), ");
@@ -1521,7 +1519,7 @@
               break;
 
             case MESSAGE_REJECT:
-#if defined(DEBUG_MSGI)
+#if defined(DEBUG_MSGI) || defined(DEBUG_TIMING)
               printk("inbound message ( MESSAGE REJECT ), ");
 #endif
               break;
@@ -1532,8 +1530,6 @@
 #endif
               break;
 
-/* my IBM drive responds to the first command with an extended message.
-   I just ignore it... */
             case EXTENDED_MESSAGE:
               { 
                 int           i, code;
@@ -1544,48 +1540,61 @@
                 make_acklow();
                 if(getphase()!=P_MSGI)
                   break;
-
+  
                 i=GETPORT(SCSIDAT);
 
 #if defined(DEBUG_MSGI)
                 printk("length (%d), ", i);
 #endif
 
-                make_acklow();
-                if(getphase()!=P_MSGI)
-                  break;
-
 #if defined(DEBUG_MSGI)
                 printk("code ( ");
 #endif
 
+                make_acklow();
+                if(getphase()!=P_MSGI)
+                  break;
+
                 code = GETPORT(SCSIDAT);
 
-#if defined(DEBUG_MSGI)
                 switch( code )
                   {
                   case 0x00:
+#if defined(DEBUG_MSGI)
                     printk("MODIFY DATA POINTER ");
+#endif
+                    SETPORT(SCSISIG, P_MSGI|ATNO);
                     break;
                   case 0x01:
+#if defined(DEBUG_MSGI)
                     printk("SYNCHRONOUS DATA TRANSFER REQUEST ");
+#endif
+                    SETPORT(SCSISIG, P_MSGI|ATNO);
                     break;
                   case 0x02:
+#if defined(DEBUG_MSGI)
                     printk("EXTENDED IDENTIFY ");
+#endif
                     break;
                   case 0x03:
+#if defined(DEBUG_MSGI)
                     printk("WIDE DATA TRANSFER REQUEST ");
+#endif
+                    SETPORT(SCSISIG, P_MSGI|ATNO);
                     break;
                   default:
+#if defined(DEBUG_MSGI)
                     if( code & 0x80 )
                       printk("reserved (%d) ", code );
                     else
                       printk("vendor specific (%d) ", code);
+#endif
+                    SETPORT(SCSISIG, P_MSGI|ATNO);
                     break;
                   }
+#if defined(DEBUG_MSGI)
                 printk(" ), data ( ");
 #endif
-
                 while( --i && (make_acklow(), getphase()==P_MSGI))
                   {
 #if defined(DEBUG_MSGI)
@@ -1597,20 +1606,27 @@
 #if defined(DEBUG_MSGI)
                 printk(" ), ");
 #endif
+                /* We reject all extended messages. To do this
+                   we just enter MSGO by asserting ATN. Since
+                   we have already identified a REJECT message
+                   will be sent. */
+                SETPORT(SCSISIG, P_MSGI|ATNO);
               }
               break;
        
             default:
               printk("unsupported inbound message %x, ", current_SC->SCp.Message);
               break;
- 
+
             }
 
           make_acklow();
           phase=getphase();
         } 
 
-      SETPORT( SCSISIG, P_MSGI );
+      /* clear SCSI fifo on BUSFREE */
+      if(phase==P_BUSFREE)
+        SETPORT(SXFRCTL0, CH1|CLRCH1);
 
       if(current_SC->SCp.phase & disconnected)
         {
@@ -1630,17 +1646,16 @@
           SETBITS( DMACNTRL0, INTEN );
           return;
         }
-
       break;
 
     case P_STATUS:                                         /* STATUS IN phase */
 #if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
       printk("STATUS, ");
 #endif
-      SETPORT( SXFRCTL0, CH1|SPIOEN);
+      SETPORT( SXFRCTL0, CH1);
 
-      SETPORT( SIMODE0, ENSPIORDY );
-      SETPORT( SIMODE1, ENPHASEMIS );
+      SETPORT( SIMODE0, 0 );
+      SETPORT( SIMODE1, ENPHASEMIS|ENREQINIT );
 
       SETBITS( SXFRCTL0, SCSIEN );
 #if defined(DEBUG_STATUS)
@@ -1652,10 +1667,15 @@
       while( TESTLO( DMASTAT, INTSTAT ) )
         ;
 
+#if 0
       if(TESTLO( SSTAT0, SPIORDY ) )
         aha152x_panic("passing STATUS phase");
+#endif
 
       current_SC->SCp.Status = GETPORT( SCSIDAT );
+      make_acklow();
+      getphase();
+
 #if defined(DEBUG_STATUS)
       printk("inbound status ");
       print_status( current_SC->SCp.Status );
@@ -1665,7 +1685,9 @@
       while( TESTHI( SXFRCTL0, SCSIEN ) )
         ;
         
+#if 0
       CLRBITS( SXFRCTL0, SPIOEN);
+#endif
       break;
 
     case P_DATAI:                                            /* DATA IN phase */
@@ -1676,16 +1698,19 @@
         printk("DATA IN, ");
 #endif
 
+        if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+          printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n",
+                 GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+
+        /* reset host fifo */
+        SETPORT(DMACNTRL0, RSTFIFO);
+        SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
+
+        SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN );
+
         SETPORT( SIMODE0, 0 );
         SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );
 
-        SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 );
-        CLRSETBITS(SXFRCTL0, CLRSTCNT, SCSIEN|DMAEN|CH1);
-
-        SETBITS(DMACNTRL0, RSTFIFO);
-        CLRSETBITS(DMACNTRL0, WRITE_READ, ENDMA);
-
-
         /* done is set when the FIFO is empty after the target left DATA IN */
         done=0;
       
@@ -1719,19 +1744,6 @@
             printk("fifodata=%d, ", fifodata);
 #endif
 
-            /* I don't know yet why, but rarely I get empty
-               buffers here, so I've to advance to the next buffer
-               before I enter the loop */
-            if(!current_SC->SCp.this_residual &&
-                current_SC->SCp.buffers_residual)
-              {
-                /* advance to next buffer */
-                current_SC->SCp.buffers_residual--;
-                current_SC->SCp.buffer++;
-                current_SC->SCp.ptr           = current_SC->SCp.buffer->address;
-                current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
-              }
-
             while( fifodata && current_SC->SCp.this_residual )
               {
                 data_count=fifodata;
@@ -1795,8 +1807,10 @@
                messages) get transfered in the data phase, so I assume 1
                additional byte is ok */
             if(fifodata>1)
-              printk("aha152x: more data than expected (%d bytes)\n",
-                     GETPORT(FIFOSTAT));
+              {
+                printk("aha152x: more data than expected (%d bytes)\n",
+                       GETPORT(FIFOSTAT));
+              }
 
 #if defined(DEBUG_DATAI)
             if(!fifodata)
@@ -1812,7 +1826,6 @@
                  current_SC->SCp.buffers_residual, 
                  current_SC->SCp.this_residual);
 #endif
-
         /* transfer can be considered ended, when SCSIEN reads back zero */
         CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
         while( TESTHI( SXFRCTL0, SCSIEN ) )
@@ -1840,9 +1853,9 @@
                current_SC->SCp.buffers_residual );
 #endif
 
-        if(GETPORT(FIFOSTAT))
+        if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT) )
           {
-            printk("%d left in FIFO, ", GETPORT(FIFOSTAT));
+            printk("%d(%d) left in FIFO, ", GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT) );
             aha152x_panic("FIFO should be empty");
           }
 
@@ -1921,16 +1934,7 @@
                (perhaps disconnect) */
 
             /* data in fifos has to be resend */
-            data_count = GETPORT(SSTAT2);
-            if( (data_count & SFCNT) == 0)
-              data_count = (data_count & SEMPTY) ? 0 : 8 ;
-            else
-              data_count &= SFCNT;
-
-#if defined(DEBUG_DATAO)
-            printk("\ntarget left DATA OUT, fifo=%d, scsififo=%d, ",
-                   GETPORT(FIFOSTAT), data_count ) ;
-#endif
+            data_count = GETPORT(SSTAT2) & (SFULL|SFCNT);
 
             data_count += GETPORT(FIFOSTAT) ;
             current_SC->SCp.ptr           -= data_count;
@@ -1942,7 +1946,7 @@
                    data_count );
 #endif
             SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
-            CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+            CLRBITS(SXFRCTL0, SCSIEN|DMAEN );
             CLRBITS(DMACNTRL0, ENDMA);
           }
         else
@@ -1977,7 +1981,7 @@
       }
       break;
 
-    case 1:                                                        /* BUSFREE */
+    case P_BUSFREE:                                                /* BUSFREE */
 #if defined(DEBUG_RACE)
       leave_driver("(BUSFREE) intr");
 #endif
@@ -1990,7 +1994,7 @@
       return;
       break;
 
-    case 3:                                     /* parity error in DATA phase */
+    case P_PARITY:                              /* parity error in DATA phase */
 #if defined(DEBUG_RACE)
       leave_driver("(DID_PARITY) intr");
 #endif
@@ -2004,9 +2008,7 @@
       break;
 
     default:
-#if defined(DEBUG_INTR)
-      printk("unexpected phase, ");
-#endif
+      printk("aha152x: unexpected phase\n");
       break;
     }
 
@@ -2192,14 +2194,7 @@
   if( s & SOFFSET)  printk("SOFFSET ");
   if( s & SEMPTY)   printk("SEMPTY ");
   if( s & SFULL)    printk("SFULL ");
-  printk("); ");
-
-
-  if(s & SFCNT)
-    s &= SFCNT;
-  else
-    s = (s & SEMPTY) ? 0 : 8;
-  printk("SFCNT ( %d ); ", s );
+  printk("); SFCNT ( %d ); ", s & (SFULL|SFCNT) );
 
 #if 0
   printk("SSTAT4 ( ");
diff --git a/drivers/scsi/aha152x.h b/drivers/scsi/aha152x.h
index e693988..7996f81 100644
--- a/drivers/scsi/aha152x.h
+++ b/drivers/scsi/aha152x.h
@@ -15,7 +15,7 @@
 int        aha152x_command(Scsi_Cmnd *);
 int        aha152x_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int        aha152x_abort(Scsi_Cmnd *, int);
-int        aha152x_reset(void);
+int        aha152x_reset(Scsi_Cmnd *);
 int        aha152x_biosparam(int, int, int*);
 
 /* number of queueable commands
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index c905002..f31d73d 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -173,6 +173,7 @@
 
 static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
 {
+    int i;
     volatile int debug = 0;
     
     /* Quick and dirty test for presence of the card. */
@@ -183,6 +184,9 @@
     /*  DEB(printk("aha1542_test_port called \n")); */
     
     outb(SRST|IRST/*|SCRST*/, CONTROL(bse));
+
+    i = jiffies + 2;
+    while (i>jiffies); /* Wait a little bit for things to settle down. */
     
     debug = 1;
     /* Expect INIT and IDLE, any of the others are bad */
@@ -294,7 +298,10 @@
 	sti();
 	/* Hmm, no mail.  Must have read it the last time around */
 	if (number_serviced) return;
-	printk("aha1542.c: interrupt received, but no mail.\n");
+	/* Virtually all of the time, this turns out to be the problem */
+	printk("aha1542.c: Unsupported BIOS options enabled."
+		"  Please turn off.\n");
+/*	printk("aha1542.c: interrupt received, but no mail.\n"); */
 	return;
       };
 
@@ -486,7 +493,7 @@
 	   (((int)sgpnt[i].address) & 1) || (sgpnt[i].length & 1)){
 	  unsigned char * ptr;
 	  printk("Bad segment list supplied to aha1542.c (%d, %d)\n",SCpnt->use_sg,i);
-	  for(i=0;i<SCpnt->use_sg++;i++){
+	  for(i=0;i<SCpnt->use_sg;i++){
 	    printk("%d: %x %x %d\n",i,(unsigned int) sgpnt[i].address, (unsigned int) sgpnt[i].alt_address,
 		   sgpnt[i].length);
 	  };
@@ -735,7 +742,7 @@
 		    DEB(aha1542_stat());
 		    
 		    DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
-		    
+		    cli();
 		    if (request_irq(irq_level,aha1542_intr_handle)) {
 			    printk("Unable to allocate IRQ for adaptec controller.\n");
 			    goto unregister;
@@ -753,7 +760,6 @@
 				    outb(dma_chan - 4, DMA_MASK_REG);
 			    }
 		    }
-		    
 		    aha_host[irq_level - 9] = shpnt;
 		    shpnt->io_port = base_io;
 		    shpnt->dma_channel = dma_chan;
@@ -761,6 +767,7 @@
 		    HOSTDATA(shpnt)->aha1542_last_mbi_used  = (2*AHA1542_MAILBOXES - 1);
 		    HOSTDATA(shpnt)->aha1542_last_mbo_used  = (AHA1542_MAILBOXES - 1);
 		    memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
+		    sti();
 #if 0
 		    DEB(printk(" *** READ CAPACITY ***\n"));
 		    
@@ -829,9 +836,14 @@
     return 0;
 }
 
-int aha1542_reset(void)
+/* We do not implement a reset function here, but the upper level code assumes
+   that it will get some kind of response for the command in SCpnt.  We must
+   oblige, or the command will hang the scsi system */
+
+int aha1542_reset(Scsi_Cmnd * SCpnt)
 {
     DEB(printk("aha1542_reset called\n"));
+    if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
     return 0;
 }
 
diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h
index 4992ffe..f906107 100644
--- a/drivers/scsi/aha1542.h
+++ b/drivers/scsi/aha1542.h
@@ -130,11 +130,12 @@
 int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int aha1542_abort(Scsi_Cmnd *, int);
 const char *aha1542_info(void);
-int aha1542_reset(void);
+int aha1542_reset(Scsi_Cmnd *);
 int aha1542_biosparam(int, int, int*);
 
 #define AHA1542_MAILBOXES 8
 #define AHA1542_SCATTER 16
+#define AHA1542_CMDLUN 1
 
 #ifndef NULL
 	#define NULL 0
@@ -147,6 +148,7 @@
 		aha1542_reset,				\
 	        NULL,		                        \
 		aha1542_biosparam,                      \
-		AHA1542_MAILBOXES, 7, AHA1542_SCATTER, 1, 0, 1}
+		AHA1542_MAILBOXES, 7, AHA1542_SCATTER, AHA1542_CMDLUN \
+		  , 0, 1}
 
 #endif
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 5ddc028..2cd1a31 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -415,7 +415,7 @@
     return internal_done_errcode;
 }
 
-/* Query the board for it's irq_level.  Nothing else matters
+/* Query the board for its irq_level.  Nothing else matters
    in enhanced mode on an EISA bus. */
 
 void aha1740_getconfig(void)
@@ -467,10 +467,10 @@
 }
 
 /* Note:  They following two functions do not apply very well to the Adaptec,
-which basically manages it's own affairs quite well without our interference,
+which basically manages its own affairs quite well without our interference,
 so I haven't put anything into them.  I can faintly imagine someone with a
 *very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
-but it hasn't happened yet, and doing aborts brings the Adaptec to it's
+but it hasn't happened yet, and doing aborts brings the Adaptec to its
 knees.  I cannot (at this moment in time) think of any reason to reset the
 card once it's running.  So there. */
 
@@ -480,9 +480,14 @@
     return 0;
 }
 
-int aha1740_reset(void)
+/* We do not implement a reset function here, but the upper level code assumes
+   that it will get some kind of response for the command in SCpnt.  We must
+   oblige, or the command will hang the scsi system */
+
+int aha1740_reset(Scsi_Cmnd * SCpnt)
 {
     DEB(printk("aha1740_reset called\n"));
+    if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
     return 0;
 }
 
diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h
index ec25861..a4bb888 100644
--- a/drivers/scsi/aha1740.h
+++ b/drivers/scsi/aha1740.h
@@ -157,7 +157,7 @@
 int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int aha1740_abort(Scsi_Cmnd *, int);
 const char *aha1740_info(void);
-int aha1740_reset(void);
+int aha1740_reset(Scsi_Cmnd *);
 int aha1740_biosparam(int, int, int*);
 
 #define AHA1740_ECBS 32
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 6364c67..b82f031 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Sun Oct 10 20:15:47 1993 by faith@cs.unc.edu
+ * Revised: Sun Oct 31 19:53:49 1993 by faith@cs.unc.edu
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.3 1993/10/11 00:16:12 root Exp $
+ * $Id: fdomain.c,v 5.6 1993/11/01 02:40:32 root Exp $
 
  * 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
@@ -144,7 +144,7 @@
 #include <linux/string.h>
 #include <linux/ioport.h>
 
-#define VERSION          "$Revision: 5.3 $"
+#define VERSION          "$Revision: 5.6 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -412,13 +412,12 @@
       if (inb( port + MSB_ID_Code ) != 0x60) return 0;
       chip = tmc18c50;
    }
-   
+
    /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
-      Now, check to be sure the bios_base matches these ports.
-      If someone was unlucky enough to have purchased more than one
-      Future Domain board, then they will have to modify this code, as
-      we only detect one board here.  [The one with the lowest bios_base.]
-    */
+      Now, check to be sure the bios_base matches these ports.  If someone
+      was unlucky enough to have purchased more than one Future Domain
+      board, then they will have to modify this code, as we only detect one
+      board here.  [The one with the lowest bios_base.]  */
 
    options = inb( port + Configuration1 );
 
@@ -426,7 +425,9 @@
    printk( " Options = %x\n", options );
 #endif
 
-   if (addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
+				/* Check for board with lowest bios_base. */
+   if (addresses[ (options & 0xc0) >> 6 ] != bios_base)
+	 return 0;
    interrupt_level = ints[ (options & 0x0e) >> 1 ];
 
    return 1;
@@ -440,7 +441,8 @@
    for (i = 0; i < 255; i++) {
       outb( i, port_base + Write_Loopback );
       result = inb( port_base + Read_Loopback );
-      if (i != result) return 1;
+      if (i != result)
+	    return 1;
    }
    return 0;
 }
@@ -503,7 +505,8 @@
 #endif
 
       for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
-	 if (port_base == ports[i]) ++flag;
+	 if (port_base == ports[i])
+	       ++flag;
       }
 
       if (flag)
@@ -571,7 +574,7 @@
    Write_FIFO_port       = port_base + Write_FIFO;
    Write_SCSI_Data_port  = port_base + Write_SCSI_Data;
 
-   fdomain_16x0_reset();
+   fdomain_16x0_reset( NULL );
 
    if (fdomain_test_loopback()) {
 #if DEBUG_DETECT
@@ -633,8 +636,7 @@
       higher level SCSI routines when I first wrote this driver.  Now,
       however, correct scan routines are part of scsi.c and these routines
       are no longer needed.  However, this code is still good for
-      debugging.
-    */
+      debugging.  */
 
    SCinit.request_buffer  = SCinit.buffer = buf;
    SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
@@ -644,7 +646,8 @@
    printk( "Future Domain detection routine scanning for devices:\n" );
    for (i = 0; i < 8; i++) {
       SCinit.target = i;
-      if (i == 6) continue;	/* The host adapter is at SCSI ID 6 */
+      if (i == scsi_hosts[this_host].this_id) /* Skip host adapter */
+	    continue;
       memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
       retcode = fdomain_16x0_command(&SCinit);
       if (!retcode) {
@@ -721,7 +724,8 @@
    timeout = jiffies + 50;	              /* 500 mS */
    while (jiffies < timeout) {
       status = inb( TMC_Status_port );        /* Read adapter status */
-      if (status & 0x02) return 0;	      /* Arbitration complete */
+      if (status & 0x02)		      /* Arbitration complete */
+	    return 0;	
    }
 
    /* Make bus idle */
@@ -731,8 +735,7 @@
    printk( "Arbitration failed, status = %x\n", status );
 #endif
 #if ERRORS_ONLY
-   printk( "Future Domain: Arbitration failed, status = %x",
-	   status );
+   printk( "Future Domain: Arbitration failed, status = %x", status );
 #endif
    return 1;
 }
@@ -777,7 +780,8 @@
       outb( 0x00, Interrupt_Cntl_port );
       fdomain_make_bus_idle();
       current_SC->result = error;
-      if (current_SC->scsi_done) current_SC->scsi_done( current_SC );
+      if (current_SC->scsi_done)
+	    current_SC->scsi_done( current_SC );
       else panic( "Future Domain: current_SC->scsi_done() == NULL" );
    } else {
       panic( "Future Domain: my_done() called outside of command\n" );
@@ -805,31 +809,20 @@
 #endif
       return;
    }
+
+   /* Abort calls my_done, so we do nothing here. */
+   if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+      printk( "Interrupt after abort, ignoring\n" );
+#endif
+      /*
+      return; */
+   }
+
 #if DEBUG_RACE
    ++in_interrupt_flag;
 #endif
 
-   if (current_SC->SCp.phase & aborted) {
-#if EVERY_ACCESS
-      if (current_SC->SCp.phase & (in_other | disconnect))
-	    printk( "aborted (%s) = %d, ",
-		    current_SC->SCp.phase & in_other
-		    ? "in_other" : "disconnect",
-		    current_SC->result );
-      else
-	    printk( "aborted = %d, ",
-		    current_SC->result );
-#endif
-      /* Force retry for timeouts after selection complete */
-      if (current_SC->SCp.phase & (in_other | disconnect)) {
-	 fdomain_16x0_reset();
-	 my_done( DID_RESET << 16 );
-      } else {
-	 my_done( current_SC->result << 16 );
-      }
-      return;
-   }
-
    if (current_SC->SCp.phase & in_arbitration) {
       status = inb( TMC_Status_port );        /* Read adapter status */
       if (!(status & 0x02)) {
@@ -888,28 +881,12 @@
       switch (status & 0x0e) {
        
       case 0x08:		/* COMMAND OUT */
-#if 0
-	 if (!current_SC->SCp.sent_command) {
-	    int i;
-	    
-	    current_SC->SCp.sent_command = COMMAND_SIZE( current_SC->cmnd[0] );
-	    
-	    for (i = 0; i < COMMAND_SIZE( current_SC->cmnd[0] ); i++) {
-	       outb( current_SC->cmnd[i], Write_SCSI_Data_port );
-#if EVERY_ACCESS
-	       printk( "CMD = %x,", current_SC->cmnd[i] );
-#endif
-	    }
-	 }
-#else
 	 outb( current_SC->cmnd[current_SC->SCp.sent_command++],
 	       Write_SCSI_Data_port );
 #if EVERY_ACCESS
 	 printk( "CMD = %x,",
 		 current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
 #endif
-	 
-#endif
 	 break;
       case 0x00:		/* DATA OUT -- tmc18c50 only */
 	 if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
@@ -1203,6 +1180,9 @@
 					  + 13));
 
 	    if (!(key == UNIT_ATTENTION && (code == 0x29 || !code))
+		&& !(key == NOT_READY
+		     && code == 0x04
+		     && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
 		&& !(key == ILLEGAL_REQUEST && (code == 0x25
 						|| code == 0x24
 						|| !code)))
@@ -1349,7 +1329,8 @@
    isr = inb( 0xa0 ) << 8;
    outb( 0x0b, 0x20 );
    isr += inb( 0x20 );
-   
+
+				/* Print out interesting information */
    printk( "IMR = 0x%04x", imr );
    if (imr & (1 << interrupt_level))
 	 printk( " (masked)" );
@@ -1357,10 +1338,12 @@
 
    printk( "SCSI Status      = 0x%02x\n", inb( SCSI_Status_port ) );
    printk( "TMC Status       = 0x%02x", inb( TMC_Status_port ) );
-   if (inb( TMC_Status_port & 1)) printk( " (interrupt)" );
+   if (inb( TMC_Status_port & 1))
+	 printk( " (interrupt)" );
    printk( "\n" );
    printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) );
-   if (inb( Interrupt_Status_port ) & 0x08) printk( " (enabled)" );
+   if (inb( Interrupt_Status_port ) & 0x08)
+	 printk( " (enabled)" );
    printk( "\n" );
    if (chip == tmc18c50) {
       printk( "FIFO Status      = 0x%02x\n", inb( port_base + FIFO_Status ) );
@@ -1411,7 +1394,7 @@
    return 0;
 }
 
-int fdomain_16x0_reset( void )
+int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
 {
 #if DEBUG_RESET
    static int called_once = 0;
@@ -1432,6 +1415,14 @@
    do_pause( 115 );
    outb( 0, SCSI_Mode_Cntl_port );
    outb( PARITY_MASK, TMC_Cntl_port );
+
+   /* Unless this is the very first call (i.e., SCPnt == NULL), everything
+      is probably hosed at this point.  We will, however, try to keep
+      things going by informing the high-level code that we need help. */
+
+   if (SCpnt)
+	 SCpnt->flags |= NEEDS_JUMPSTART;
+   
    return 0;
 }
 
diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h
index d913e92..3374232 100644
--- a/drivers/scsi/fdomain.h
+++ b/drivers/scsi/fdomain.h
@@ -4,7 +4,7 @@
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993 Rickard E. Faith
  *
- * $Id: fdomain.h,v 5.1 1993/10/10 13:33:11 root Exp $
+ * $Id: fdomain.h,v 5.2 1993/10/24 16:40:41 root Exp $
 
  * 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
@@ -25,7 +25,7 @@
 int        fdomain_16x0_command( Scsi_Cmnd * );
 int        fdomain_16x0_abort( Scsi_Cmnd *, int );
 const char *fdomain_16x0_info( void );
-int        fdomain_16x0_reset( void ); 
+int        fdomain_16x0_reset( Scsi_Cmnd * ); 
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
 int        fdomain_16x0_biosparam( int, int, int * );
 
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
index cb55068..88a536f 100644
--- a/drivers/scsi/g_NCR5380.h
+++ b/drivers/scsi/g_NCR5380.h
@@ -37,7 +37,7 @@
 int generic_NCR5380_detect(int);
 const char *generic_NCR5380_info(void);
 int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int generic_NCR5380_reset(void);
+int generic_NCR5380_reset(Scsi_Cmnd *);
 
 
 #ifndef NULL
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index 69e67b8..3780d72 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -21,6 +21,14 @@
 */
 
 
+/* A jumpstart is often required when the reset() function is called -
+   many host adapters cannot do this cleanly, so they do nothing at all.
+   To get the command going again, these routines set this bit in the flags
+   so that a scsi_request_sense() is executed, and the command starts running
+   again */
+
+#define NEEDS_JUMPSTART 0x20
+
 #define SG_NONE 0
 #define SG_ALL 0xff
 
@@ -114,9 +122,15 @@
 	/*
 		The reset function will reset the SCSI bus.  Any executing 
 		commands should fail with a DID_RESET in the host byte.
+		The Scsi_Cmnd  is passed so that the reset routine can figure
+		out which host adapter should be reset, and also which command
+		within the command block was responsible for the reset in
+		the first place.  Some hosts do not implement a reset function,
+		and these hosts must call scsi_request_sense(SCpnt) to keep
+		the command alive.
 	*/ 
 
-	int (* reset)(void);
+	int (* reset)(Scsi_Cmnd *);
 	/*
 		This function is used to select synchronous communications,
 		which will result in a higher data throughput.  Not implemented
diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h
index d3b9bb0..1ebacb8 100644
--- a/drivers/scsi/pas16.h
+++ b/drivers/scsi/pas16.h
@@ -119,7 +119,7 @@
 int pas16_detect(int);
 const char *pas16_info(void);
 int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int pas16_reset(void);
+int pas16_reset(Scsi_Cmnd *);
 
 #ifndef NULL
 #define NULL 0
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index ada400a..9bf092a 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -85,6 +85,7 @@
 #define WAS_SENSE	0x04
 #define IS_RESETTING	0x08
 #define ASKED_FOR_SENSE 0x10
+/* #define NEEDS_JUMPSTART 0x20  defined in hosts.h */
 
 /*
  *	This is the number  of clock ticks we should wait before we time out 
@@ -698,6 +699,9 @@
 	int clock;
 #endif
 
+	if ((unsigned long) &SCpnt < current->kernel_stack_page)
+	  panic("Kernel stack overflow.");
+
 	host = SCpnt->host;
 
 /*
@@ -881,13 +885,14 @@
 	SCpnt->flags |= (WAS_RESET | IS_RESETTING);
 	scsi_reset(SCpnt);
 	
-#if 0
 #ifdef DEBUG
 	printk("performing request sense\n");
 #endif
 	
-	scsi_request_sense (SCpnt);
-#endif
+	if(SCpnt->flags & NEEDS_JUMPSTART) {
+	  SCpnt->flags &= ~NEEDS_JUMPSTART;
+	  scsi_request_sense (SCpnt);
+	};
 }
 	
 	
@@ -1344,14 +1349,14 @@
 				  SCpnt1 = SCpnt1->next;
 				};
 
-				temp = host->hostt->reset();			
+				temp = host->hostt->reset(SCpnt);	
 				}				
 			else
 				{
 				host->host_busy++;
 	
 				sti();
-				temp = host->hostt->reset();
+				temp = host->hostt->reset(SCpnt);
 				host->last_reset = jiffies;
 				host->host_busy--;
 				}
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 45a6c5c..08a2035 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -446,7 +446,7 @@
 extern unsigned long sg_init1(unsigned long, unsigned long);
 extern void sg_attach(Scsi_Device *);
 
-#if defined(MAJOR_NR) && (MAJOR_NR != 9)
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
 static void end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
 {
 	struct request * req;
@@ -456,8 +456,8 @@
 	req = &SCpnt->request;
 	req->errors = 0;
 	if (!uptodate) {
-		printk(DEVICE_NAME " I/O error\n");
-		printk("dev %04x, sector %d\n",req->dev,req->sector);
+		printk(DEVICE_NAME " I/O error: dev %04x, sector %lu\n",
+		       req->dev,req->sector);
 	}
 
 	do {
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index ffda507..4fe973e 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -18,6 +18,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
+
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -25,8 +26,7 @@
 /* Number of real scsi disks that will be detected ahead of time */
 static int NR_REAL=-1;
 
-#define NR_BLK_DEV	12
-#define MAJOR_NR 8
+#define MAJOR_NR SCSI_DISK_MAJOR
 #define START_PARTITION 4
 #define SCSI_DEBUG_TIMER 20
 /* Number of jiffies to wait before completing a command */
@@ -487,7 +487,7 @@
   return 0;
 }
 
-int scsi_debug_reset(void)
+int scsi_debug_reset(Scsi_Cmnd * SCpnt)
 {
     int i;
     void (*my_done)(Scsi_Cmnd *);
diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h
index 263fda3..725b37e 100644
--- a/drivers/scsi/scsi_debug.h
+++ b/drivers/scsi/scsi_debug.h
@@ -8,7 +8,7 @@
 int scsi_debug_abort(Scsi_Cmnd *, int);
 int scsi_debug_biosparam(int, int*);
 char *scsi_debug_info(void);
-int scsi_debug_reset(void);
+int scsi_debug_reset(Scsi_Cmnd *);
 
 #ifndef NULL
 	#define NULL 0
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index 64835f6..a5ea183 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -177,8 +177,7 @@
 	
 	SCpnt = allocate_device(NULL, dev->index, 1);
 
-	scsi_do_cmd(SCpnt,  cmd,  buf,  ((outlen > MAX_BUF) ? 
-			MAX_BUF : outlen),  scsi_ioctl_done,  MAX_TIMEOUT, 
+	scsi_do_cmd(SCpnt,  cmd,  buf, needed,  scsi_ioctl_done,  MAX_TIMEOUT, 
 			MAX_RETRIES);
 
 	if (SCpnt->request.dev != 0xfffe){
@@ -187,10 +186,20 @@
 	  while (SCpnt->request.dev != 0xfffe) schedule();
 	};
 
-	result = verify_area(VERIFY_WRITE, cmd_in, (outlen > MAX_BUF) ? MAX_BUF  : outlen);
-	if (result)
-		return result;
-	memcpy_tofs ((void *) cmd_in,  buf,  (outlen > MAX_BUF) ? MAX_BUF  : outlen);
+
+	/* If there was an error condition, pass the info back to the user. */
+	if(SCpnt->result) {
+	  result = verify_area(VERIFY_WRITE, cmd_in, sizeof(SCpnt->sense_buffer));
+	  if (result)
+	    return result;
+	  memcpy_tofs((void *) cmd_in,  SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
+	} else {
+
+	  result = verify_area(VERIFY_WRITE, cmd_in, (outlen > MAX_BUF) ? MAX_BUF  : outlen);
+	  if (result)
+	    return result;
+	  memcpy_tofs ((void *) cmd_in,  buf,  (outlen > MAX_BUF) ? MAX_BUF  : outlen);
+	};
 	result = SCpnt->result;
 	SCpnt->request.dev = -1;  /* Mark as not busy */
 	if (buf) scsi_free(buf, needed);
@@ -249,6 +258,7 @@
 		case SCSI_IOCTL_PROBE_HOST:
 			return ioctl_probe(dev->host, arg);
 		case SCSI_IOCTL_SEND_COMMAND:
+			if(!suser())  return -EACCES;
 			return ioctl_command((Scsi_Device *) dev, arg);
 		case SCSI_IOCTL_DOORLOCK:
 			if (!dev->removable || !dev->lockable) return 0;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 712cea6..7bb847a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -17,9 +17,7 @@
 #include <linux/errno.h>
 #include <asm/system.h>
 
-
-#define MAJOR_NR 8
-
+#define MAJOR_NR SCSI_DISK_MAJOR
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -204,9 +202,8 @@
   The SCpnt->request.nr_sectors field is always done in 512 byte sectors,
   even if this really isn't the case.
 */
-	    printk("sd.c: linked page request. (%x %x)",
+	    panic("sd.c: linked page request (%lx %x)",
 		  SCpnt->request.sector, this_count);
-	    panic("Aiiiiiiiiiiiieeeeeeeee");
 	  }
       }
     end_scsi_request(SCpnt, 1, this_count);
@@ -262,13 +259,15 @@
 
 	  if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
 	    if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+	      if(rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->removable) {
 	      /* detected disc change.  set a bit and quietly refuse	*/
 	      /* further access.					*/
 	      
-	      rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->changed = 1;
-	      end_scsi_request(SCpnt, 0, this_count);
-	      requeue_sd_request(SCpnt);
-	      return;
+		rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->changed = 1;
+		end_scsi_request(SCpnt, 0, this_count);
+		requeue_sd_request(SCpnt);
+		return;
+	      }
 	    }
 	  }
 	  
@@ -705,6 +704,7 @@
 	 SCpnt->sense_buffer[2] == NOT_READY) {
 	int time1;
 	if(!spintime){
+	  printk( "sd%d: Spinning up disk...", i );
 	  cmd[0] = START_STOP;
 	  cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
 	  cmd[1] |= 1;  /* Return immediately */
@@ -726,8 +726,15 @@
 
 	time1 = jiffies;
 	while(jiffies < time1 + 100); /* Wait 1 second for next try */
+	printk( "." );
       };
-    } while(the_result && spintime && spintime+1500 < jiffies);
+    } while(the_result && spintime && spintime+5000 > jiffies);
+    if (spintime) {
+       if (the_result)
+           printk( "not responding...\n" );
+       else
+           printk( "ready\n" );
+    }
   };  /* current == task[0] */
 
 
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
index d5fb319..32baed7 100644
--- a/drivers/scsi/seagate.c
+++ b/drivers/scsi/seagate.c
@@ -34,7 +34,7 @@
  *
  * -DARBITRATE will cause the host adapter to arbitrate for the 
  *	bus for better SCSI-II compatability, rather than just 
- *	waiting for BUS FREE and then doing it's thing.  Should
+ *	waiting for BUS FREE and then doing its thing.  Should
  *	let us do one command per Lun when I integrate my 
  *	reorganization changes into the distribution sources.
  *
@@ -884,7 +884,7 @@
 			if (STATUS & STAT_BSY) {
 				printk("scsi%d : BST asserted after we've been aborted.\n",
 					hostno);
-				seagate_st0x_reset();
+				seagate_st0x_reset(NULL);
 				return retcode(DID_RESET);
 			}
 			return retcode(st0x_aborted);
@@ -1478,7 +1478,7 @@
 #ifdef notyet
 	if (st0x_aborted) {
 		if (STATUS & STAT_BSY) {	
-			seagate_st0x_reset();
+			seagate_st0x_reset(NULL);
 			st0x_aborted = DID_RESET;
 		} 
 		abort_confirm = 1;
@@ -1560,7 +1560,7 @@
 	the seagate_st0x_reset function resets the SCSI bus
 */
 	
-int seagate_st0x_reset (void)
+int seagate_st0x_reset (Scsi_Cmnd * SCpnt)
 	{
 	unsigned clock;
 	/*
@@ -1590,6 +1590,7 @@
 #ifdef DEBUG
 	printk("SCSI bus reset.\n");
 #endif
+	if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
 	return 0;
 	}
 
diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h
index 0be87ab..fff3e39 100644
--- a/drivers/scsi/seagate.h
+++ b/drivers/scsi/seagate.h
@@ -18,7 +18,7 @@
 
 int seagate_st0x_abort(Scsi_Cmnd *, int);
 const char *seagate_st0x_info(void);
-int seagate_st0x_reset(void); 
+int seagate_st0x_reset(Scsi_Cmnd *); 
 
 #ifndef NULL
 	#define NULL 0
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 7509699..39e11d4 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -24,8 +24,6 @@
 #include "scsi_ioctl.h"
 #include "sg.h"
 
-#define MAJOR_NR 21
-
 int NR_SG=0;
 int MAX_SG=0;
 
@@ -296,9 +294,10 @@
 /* Driver initialization */
 unsigned long sg_init(unsigned long mem_start, unsigned long mem_end)
  {
-  if (register_chrdev(MAJOR_NR,"sg",&sg_fops)) 
+  if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) 
    {
-    printk("Unable to get major %d for Generic SCSI device\n",MAJOR_NR);
+    printk("Unable to get major %d for generic SCSI device\n",
+	   SCSI_GENERIC_MAJOR);
     return mem_start;
    }
   if (NR_SG == 0) return mem_start;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index b86e30d..59a0cd2 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -20,8 +20,7 @@
 #include <linux/errno.h>
 #include <asm/system.h>
 
-#define MAJOR_NR 11
-
+#define MAJOR_NR SCSI_CDROM_MAJOR
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -127,7 +126,7 @@
 			int offset;
 			offset = (SCpnt->request.sector % 4) << 9;
 			memcpy((char *)SCpnt->request.buffer, 
-			       SCpnt->buffer + offset, 
+			       (char *)SCpnt->buffer + offset, 
 			       this_count << 9);
 			/* Even though we are not using scatter-gather, we look
 			   ahead and see if there is a linked request for the
@@ -139,7 +138,7 @@
 			   SCpnt->request.bh->b_reqnext &&
 			   SCpnt->request.bh->b_reqnext->b_size == 1024) {
 			  memcpy((char *)SCpnt->request.bh->b_reqnext->b_data, 
-				 SCpnt->buffer + 1024, 
+				 (char *)SCpnt->buffer + 1024, 
 				 1024);
 			  this_count += 2;
 			};
@@ -173,11 +172,8 @@
 			{	 
 			SCpnt->request.errors = 0;
 			if (!SCpnt->request.bh)
-			  {
-			    printk("sr.c: linked page request. (%x %x)",
+			    panic("sr.c: linked page request (%lx %x)",
 				  SCpnt->request.sector, this_count);
-			    panic("Aiiiiiiiiiiiieeeeeeeee");
-			  }
 			}
 
 		  end_scsi_request(SCpnt, 1, this_count);  /* All done */
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 06e8148..a138655 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -47,7 +47,7 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#define MAJOR_NR 9
+#define MAJOR_NR SCSI_TAPE_MAJOR
 #include "../block/blk.h"
 #include "scsi.h"
 #include "scsi_ioctl.h"
diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h
index 39e1ba2..453118a 100644
--- a/drivers/scsi/t128.h
+++ b/drivers/scsi/t128.h
@@ -96,7 +96,7 @@
 int t128_detect(int);
 const char *t128_info(void);
 int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int t128_reset(void);
+int t128_reset(Scsi_Cmnd *);
 
 #ifndef NULL
 #define NULL 0
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
index 9b03cf5..d59398b 100644
--- a/drivers/scsi/ultrastor.c
+++ b/drivers/scsi/ultrastor.c
@@ -926,7 +926,7 @@
 
 }
 
-int ultrastor_reset(void)
+int ultrastor_reset(Scsi_Cmnd * SCpnt)
 {
     int flags;
     register int i;
@@ -934,7 +934,11 @@
     printk("US14F: reset: called\n");
 #endif
 
-    if(config.slot) return 0;  /* Do not attempt a reset for the 24f */
+    if(config.slot) {
+      if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
+      return 0;  /* Do not attempt a reset for the 24f */
+    };
+
     save_flags(flags);
     cli();
 
@@ -1011,7 +1015,7 @@
 	printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp);
 	/* A command has been lost.  Reset and report an error
 	   for all commands.  */
-	ultrastor_reset();
+	ultrastor_reset(NULL);
 	return;
     }
 #endif
diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h
index dbbaa7c..0731227 100644
--- a/drivers/scsi/ultrastor.h
+++ b/drivers/scsi/ultrastor.h
@@ -17,7 +17,7 @@
 const char *ultrastor_info(void);
 int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int ultrastor_abort(Scsi_Cmnd *, int);
-int ultrastor_reset(void);
+int ultrastor_reset(Scsi_Cmnd *);
 int ultrastor_biosparam(int, int, int *);
 
 #define ULTRASTOR_14F_MAX_SG 16
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index bc21559..059dbfb 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -592,11 +592,16 @@
 }
 
 
-int wd7000_reset(void)
+/* We do not implement a reset function here, but the upper level code assumes
+   that it will get some kind of response for the command in SCpnt.  We must
+   oblige, or the command will hang the scsi system */
+
+int wd7000_reset(Scsi_Cmnd * SCpnt)
 {
 #ifdef DEBUG
     printk("wd7000_reset\n");
 #endif
+    if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
     return 0;
 }
 
diff --git a/drivers/scsi/wd7000.h b/drivers/scsi/wd7000.h
index bc27c61..ab8b2a5 100644
--- a/drivers/scsi/wd7000.h
+++ b/drivers/scsi/wd7000.h
@@ -172,7 +172,7 @@
 int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int wd7000_abort(Scsi_Cmnd *, int);
 const char *wd7000_info(void);
-int wd7000_reset(void);
+int wd7000_reset(Scsi_Cmnd *);
 int wd7000_biosparam(int, int, int*);
 
 #ifndef NULL
diff --git a/drivers/sound/.blurb b/drivers/sound/.blurb
new file mode 100644
index 0000000..1b2fa63
--- /dev/null
+++ b/drivers/sound/.blurb
@@ -0,0 +1,8 @@
+NOTE!	This driver version is not compatible with the version 1.0c.
+	This means you have to use the latest version of the snd-util
+	package. The earlier ones (from 1.0) will not work. If you have
+	other programs using ioctl calls of the driver, they must be
+	recompiled. Most of them will not work without some source
+	modifications.
+
+		Hannu
diff --git a/drivers/sound/.indent.pro b/drivers/sound/.indent.pro
new file mode 100644
index 0000000..f10c3dd
--- /dev/null
+++ b/drivers/sound/.indent.pro
@@ -0,0 +1,11 @@
+-bad 
+-bap 
+-fca
+-fc1 
+-cdb 
+-sc 
+-bl 
+-psl 
+-di16
+-lp
+-ip5
diff --git a/drivers/sound/COPYING b/drivers/sound/COPYING
new file mode 100644
index 0000000..d1509c5
--- /dev/null
+++ b/drivers/sound/COPYING
@@ -0,0 +1,25 @@
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 85e5ccd..76d1857 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -1,14 +1,13 @@
-# Makefile for the stub version of Linux sound card driver
-#
-# 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).
+# Makefile for the Linux sound card driver
 #
 # Note 2! The CFLAGS definitions are now inherited from the
 # parent makes. (hopefully)
 #
 #
 
+VERSION		= 2.0
+USRINCDIR	= /usr/include
+
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 .s.o:
@@ -16,31 +15,47 @@
 .c.o:
 	$(CC) $(CFLAGS) -c $<
 
-OBJS	= sound_stub.o
+OBJS   = soundcard.o dsp.o audio.o dmabuf.o sb_dsp.o dev_table.o \
+	 opl3.o sequencer.o midibuf.o sb_card.o pas2_card.o adlib_card.o \
+	 pas2_pcm.o pas2_mixer.o pas2_midi.o gus_card.o gus_wave.o mpu401.o \
+	 gus_midi.o gus_vol.o patmgr.o
+
+all:	local.h sound.a
 
 sound.a: $(OBJS) 
+	-rm -f sound.a
 	$(AR) rcs sound.a $(OBJS)
 	sync
 
-clean:
-	rm -f core *.o *.a
+indent:
+	for n in *.c;do echo indent $$n;indent $$n;done
 
-config:
-	-@if [ -n "$(CONFIG_SOUND)" ] ;then make notice; fi
+local.h:
+	rm -f configure
+	$(MAKE) config
+	$(MAKE) dep
 
-notice:
-	@echo
-	@echo
-	@echo "WARNING!	You have attempted to compile the sound driver"
-	@echo "		to your kernel. The driver is not included in"
-	@echo "		the kernel distribution. You have to install"
-	@echo "		the sound driver and to run "make config" again."
-	@echo "		Otherwise the sound driver will not be included"
-	@echo
-	@echo "		The sound driver is available at nic.funet.fi"
-	@echo "		in directory pub/OS/Linux/xtra/snd-kit. It "
-	@echo "		should be also at the other Linux ftp sites."
-	@echo
+config: configure
+	@./configure > local.h
+	@echo \#define SOUND_VERSION_STRING \"$(VERSION)\" >> local.h
+	@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
+	@echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
+	@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h
+	@echo \#define SOUND_CONFIG_DOMAIN \"`domainname`\" >> local.h
+
+clrconf:
+	rm -f local.h .depend os.h soundcard.c
+
+configure: configure.c
+	$(HOSTCC) -o configure configure.c
+	@cat .blurb
 
 dep:
-	@touch .depend
+	$(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/drivers/sound/Readme b/drivers/sound/Readme
new file mode 100644
index 0000000..1357cc3
--- /dev/null
+++ b/drivers/sound/Readme
@@ -0,0 +1,272 @@
+Release notes for the Linux Sound Driver 2.1
+-----------------------------------------------
+
+This version is just version 2.0 with some kind of SB16
+support. The SB16 works now in 8 bit mono mode up to speed
+44100 Hz. Stereo recording and playback is not supported with
+the SB16 yet. Also the 16 bit mode is missing.
+
+You will need the snd-util-2.0.tar.gz and snd-data-0.1.tar.Z
+packages to use this driver. They should be in the same
+ftp site or BBS from where you got this driver. For
+example at nic.funet.fi:pub/OS/Linux/*.
+
+If you are looking for the installation instructions, please
+look at linux/Readme.
+
+Welcome to use the Gravis UltraSound driver for Linux. This
+driver still supports the same cards than version 1.0c 
+(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib). 
+In addition there is rather limited support for MPU-401
+(and compatible) midi cards. Also the OPL-3 synthesizer
+of the SB Pro and PAS16 cards is now supported in the 4 OP
+modes.
+Most of the features of the /dev/sequencer device file are
+available just for GUS owners. 
+
+The SoundBlaster 16 and SB 16 ASP cards are not supported.
+They could work in mono mode with speeds < 22 kHz. 
+The OPL-3 chicp of the SB 16 should work but it doesn't.
+
+NOTE!	There are separate driver for CD-ROMS supported by
+	some soundcards. The driver for CDU31A (Fusion 16) is
+	called cdu31a-0.6.diff.z. It will be contained in the
+	Linux version 0.99.12. The driver for the CD-ROM of SB Pro
+	is sbpcd0.4.tar.gz (these were the latest versions when I wrote
+	this). These files should be at least at sunsite.unc.edu.
+	Also the SCSI interface of the PAS16 should be supported by
+	Linux 0.99.13k and later.
+
+	There is also a driver for joystick. Look for file joystick-0.5.tar.gz
+	(sunsite).
+
+
+Compatibility with the earlier versions
+---------------------------------------
+
+IMPORTANT!!!!!!!!!!!!!!!!!!!!!!
+
+This version is not binary or source compatible with the version 1.0c.
+
+The ioctl() interface has changed completely since version 1.0c. All
+programs using this driver must be at least recompiled. 
+The snd-util-2.0 package contains some utilities for this version.
+
+The version 1.0c and earlier used a 'nonportable' ioctl calling scheme
+where the input argument was passed by value and the output value was
+returned as the functional return. For example setting the speed of
+/dev/dsp were done as the following:
+
+	int actual_speed;
+	actual_speed = ioctl(fd, SOUND_PCM_WRITE_RATE, 44100);
+
+After version 1.99.0 this must be done as the following:
+
+	int actual_speed = 44100;
+	ioctl(fd, SOUND_PCM_WRITE_RATE, &actual_speed);
+
+If you have an application written for the version 1.0, you should search
+for the strings SNDCTL_ and SOUND_ and to check the parameters. 
+The following ioctl calls have changed:
+
+	SNDCTL_SEQ_GETOUTCOUNT
+	SNDCTL_SEQ_GETINCOUNT
+	SNDCTL_SEQ_TESTMIDI
+	SNDCTL_DSP_SPEED
+	SNDCTL_DSP_STEREO
+	SNDCTL_DSP_GETBLKSIZE
+	SNDCTL_DSP_SAMPLESIZE
+	SOUND_PCM_WRITE_CHANNELS
+	SOUND_PCM_WRITE_FILTER
+	SOUND_PCM_READ_RATE
+	SOUND_PCM_READ_CHANNELS
+	SOUND_PCM_READ_BITS
+	SOUND_PCM_READ_FILTER
+	SOUND_PCM_WRITE_BITS
+	SOUND_PCM_WRITE_RATE
+	SOUND_MIXER_READ_*	(several ones)
+	SOUND_MIXER_WRITE_*	(several ones)
+
+Since the this version will support more than one synthesizer devices
+at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition
+there is some new fields which must be initialized. Look at the sbiset.c in
+the snd-util-2.0 package for further info.
+
+This version is almost 100% compatible with the alpha test version (1.99.9). The
+difference is in the installation procedure.
+
+Using the driver under other environments than Linux
+----------------------------------------------------
+
+**** There is some ISC stuff in this driver. Please stay away ****
+This stuff is here just because I want to be in sync with the porters. This
+ports don't work yet.
+The ISC port is by Andy Warner (andy@harris.nl). 
+
+There FreeBSD port is by 
+There is a FreeBSD port by Jim Lowe (james@blatz.cs.uwm.edu) in the directory
+freebsd. (Based on my 386bsd port which was in the previous release (1.99.9).
+It's should work but it requires still some testing. It should work
+with the NetBSD 0.9 also but propably not with the 386bsd 0.1.
+There has been some problems with GUS in SCO and NetBSD.
+In addition I'm preparing a SCO port myself. Yet again, these ports
+are little incomplete and untested. It could be possible to get them to work
+with finite amount of hacking but be careful. 
+
+New features
+------------
+
+There is also some changes which make this version more usable than
+the version 1.0c.
+
+- /dev/dsp and /dev/audio
+
+The DMA buffering is now little bit more intelligent than earlier. The
+buffer size is selected run-time so that a buffer holds data for 0.5 to
+1.0 seconds of recording or playback. This makes recording more comfortable
+than with version 1.0. With the previous version there was sometimes more
+than 10 seconds of delay before the driver returned the first input byte.
+
+There is also support for more than one digitized voice devices. The device
+files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16. 
+The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself 
+and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two
+dsp/audio devices are available also if you have combination of SB and GUS.
+With GUS and PAS16 you will have even three dsp/audio devices. These devices
+can be used independently and can be active at the same time (3 channels
+at the same time propably don't work).
+
+The dsp/audio support of PAS16 should be much cleaner now since the
+constant clicking sound between the DMA blocks (about once per second) has
+been eliminated.
+
+The stereo playback of GUS doesn't work perfectly. There is lot of 
+clicking in the output.
+
+- /dev/mixer
+
+No changes.
+
+There is no mixer for the GUS yet.
+
+- /dev/sequencer
+
+This part has the most changes. Mostly to support the rich
+features of the Gravis UltraSound. There is also the support
+for the OPL-3 synthesizer chip.
+
+- /dev/sndstat
+
+This is a new devicefile for debugging purposes. A better place for
+it is in the /proc -directory but I was just too lazy to implement it
+properly. The /dev/sndstat (major 14, minor 6) is a file which returns
+info about the current configuration (see the example below). If you
+send me a error/problem report, please include a printout from this 
+device to your message (cat /dev/sndstat).
+
+Note!	This device file is currently present only in the Linux version
+	of this driver.
+
+------ cut here --- cat /dev/sndstat example --------
+Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
+Config options: 0x00000d4b
+
+Major number: 14
+HW config: 
+Type 4: Gravis Ultrasound  at 0x210 irq 15 drq 6
+Type 3: ProAudioSpectrum  at 0x388 irq 10 drq 3
+Type 2: SoundBlaster  at 0x220 irq 7 drq 1
+Type 1: AdLib  at 0x388 irq 0 drq 0
+
+PCM devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+02: SoundBlaster 2.0
+
+Synth devices:
+00: Gravis UltraSound
+01: Yamaha OPL-3
+
+Midi devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+
+Mixer(s) installed
+------ cut here ---- End of Example -----------
+
+
+Known bugs
+----------
+
+- There was clicking during stereo playback to /dev/dsp with GUS.
+  * Fixed in 1.99.9 *
+- It's not possible to open /dev/dsp (or /dev/audio) while the 
+  /dev/sequencer is open for output and GUS is the only soundcard
+  installed. It's possible if /dev/dsp is opened before /dev/sequencer
+  but at this time the GUS is not available for access via /dev/sequencer.
+  This is a limitation of the driver.
+- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
+  It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
+  adapter.
+- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
+  ^C and playing again should solve this problem. This is propably caused by
+  incompatibilities between GUS and certain VLB motherboards. Try to avoid
+  switching between VTs while patches are being loaded to the GUS.
+- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed
+  in 1.99.9.
+- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9.
+- There is a skeleton of the patch manager support. It don't work in
+  this version. 
+
+
+Future development
+------------------
+
+- The SB16 card should be fully supported some day. The SDK for it is
+  not available yet so it's not possible to implement the 16 bit mode yet.
+- Since this driver is no longer just the Linux Sound Driver, it's time
+  to give it a new name. I have planned to use name VoxWare.
+- I'm writing a Hacker's guide to the VoxWare sound driver. Should
+  be ready within this year (alpha version).
+- Completion of the ISC, SCO and BSD ports. Port to SVR4.2.
+- I'm interested to implement/include support for new soundcards and 
+  operating systems. 
+
+  Hint for the soundcard and OS manufacturers:
+  I'm collecting soundcards (high end ones) and SDKs for them. In
+  addition I'm collecting PC operating systems. I will be happy if
+  somebody sends me such items. In addition such kind of donation
+  makes it easier to change the VoxWare driver to support your
+  soundcard or operating system. However, please contact me before
+  sending anything.
+
+I will propably release some fix versions within this and next year. At
+least when the non-Linux versions get ready. The next major release (3.0)
+will be quite complete rewrite and released after about a year (end of 94 or
+beginning of 95).
+
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given usefull suggestions. The following is a list of major
+contributors. (I could have forgotten some names.)
+
+	Craig Metz	1/2 of the PAS16 Mixer and PCM support
+	Rob Hooft	Volume computation algorithm for the FM synth.
+	Mika Liljeberg	uLaw encoding and decoding routines
+	Greg Lee	Volume computation algorithm for the GUS and
+			lot's of valuable suggestions.
+	Andy Warner	ISC port
+	Jim Lowe	FreeBSD port
+
+Regards,
+
+Hannu Savolainen
+hsavolai@cs.helsinki.fi
+
+Snail mail:	Hannu Savolainen
+		Pallaksentie 4 A 2
+		00970 Helsinki
+		Finland
diff --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c
new file mode 100644
index 0000000..29e521e
--- /dev/null
+++ b/drivers/sound/adlib_card.c
@@ -0,0 +1,52 @@
+
+/*
+ * linux/kernel/chr_drv/sound/adlib_card.c
+ * 
+ * Detection routine for the AdLib card.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+long
+attach_adlib_card (long mem_start, struct address_info *hw_config)
+{
+
+  if (opl3_detect (FM_MONO))
+    {
+      mem_start = opl3_init (mem_start);
+    }
+  return mem_start;
+}
+
+int
+probe_adlib (struct address_info *hw_config)
+{
+  return opl3_detect (FM_MONO);
+}
+
+#endif
diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c
new file mode 100644
index 0000000..775e6ab
--- /dev/null
+++ b/drivers/sound/audio.c
@@ -0,0 +1,298 @@
+/*
+ * linux/kernel/chr_drv/sound/audio.c
+ * 
+ * Device file manager for /dev/audio
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+#ifndef EXCLUDE_AUDIO
+
+#include "ulaw.h"
+
+#define ON		1
+#define OFF		0
+
+static int      wr_buff_no[MAX_DSP_DEV];	/* != -1, if there is a
+						 * incomplete output block */
+static int      wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
+static char    *wr_dma_buf[MAX_DSP_DEV];
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+  int             mode;
+  int             ret;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  if ((ret = DMAbuf_open (dev, mode)) < 0)
+    return ret;
+
+  wr_buff_no[dev] = -1;
+  return ret;
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+  int             mode;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  if (wr_buff_no[dev] >= 0)
+    {
+      DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+      wr_buff_no[dev] = -1;
+    }
+
+  DMAbuf_release (dev, mode);
+}
+
+#ifdef NO_INLINE_ASM
+static void
+translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
+{
+  unsigned long   i;
+
+  for (i = 0; i < n; ++i)
+    buff[i] = table[buff[i]];
+}
+
+#else
+extern inline void
+translate_bytes (const void *table, void *buff, unsigned long n)
+{
+  __asm__ ("cld\n"
+	   "1:\tlodsb\n\t"
+	   "xlatb\n\t"
+	   "stosb\n\t"
+	   "loop 1b\n\t":
+	   :"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
+	   :"bx", "cx", "di", "si", "ax");
+}
+
+#endif
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  int             c, p, l;
+  int             err;
+
+  dev = dev >> 4;
+
+  p = 0;
+  c = count;
+
+  if (!count)			/* Flush output */
+    {
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return 0;
+    }
+
+  while (c)
+    {				/* Perform output blocking */
+      if (wr_buff_no[dev] < 0)	/* There is no incomplete buffers */
+	{
+	  if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
+	    return wr_buff_no[dev];
+	  wr_buff_ptr[dev] = 0;
+	}
+
+      l = c;
+      if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
+	l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
+
+      COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+
+      /* Insert local processing here */
+
+#ifdef linux
+      /* This just allows interrupts while the conversion is running */
+      __asm__ ("sti");
+#endif
+      translate_bytes (ulaw_dsp, &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+
+      c -= l;
+      p += l;
+      wr_buff_ptr[dev] += l;
+
+      if (wr_buff_ptr[dev] >= wr_buff_size[dev])
+	{
+	  if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
+	    return err;
+
+	  wr_buff_no[dev] = -1;
+	}
+
+    }
+
+  return count;
+}
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  int             c, p, l;
+  char           *dmabuf;
+  int             buff_no;
+
+  dev = dev >> 4;
+  p = 0;
+  c = count;
+
+  while (c)
+    {
+      if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
+	return buff_no;
+
+      if (l > c)
+	l = c;
+
+      /* Insert any local processing here. */
+#ifdef linux
+      /* This just allows interrupts while the conversion is running */
+      __asm__ ("sti");
+#endif
+
+      translate_bytes (dsp_ulaw, dmabuf, l);
+
+      COPY_TO_USER (buf, p, dmabuf, l);
+
+      DMAbuf_rmchars (dev, buff_no, l);
+
+      p += l;
+      c -= l;
+    }
+
+  return count - c;
+}
+
+int
+audio_ioctl (int dev, struct fileinfo *file,
+	     unsigned int cmd, unsigned int arg)
+{
+  dev = dev >> 4;
+
+  switch (cmd)
+    {
+    case SNDCTL_DSP_SYNC:
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+      break;
+
+    case SNDCTL_DSP_POST:
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return 0;
+      break;
+
+    case SNDCTL_DSP_RESET:
+      wr_buff_no[dev] = -1;
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+      break;
+
+    default:
+#if 1
+      return RET_ERROR (EIO);
+#else
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+#endif
+    }
+}
+
+long
+audio_init (long mem_start)
+{
+  return mem_start;
+}
+
+#else
+/* Stub versions */
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+  {
+    return RET_ERROR (ENXIO);
+  }
+
+void
+audio_release (int dev, struct fileinfo *file)
+  {
+  };
+int
+audio_ioctl (int dev, struct fileinfo *file,
+	     unsigned int cmd, unsigned int arg)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+  return RET_ERROR (EIO);
+}
+
+long
+audio_init (long mem_start)
+{
+  return mem_start;
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/configure.c b/drivers/sound/configure.c
new file mode 100644
index 0000000..e27bf75
--- /dev/null
+++ b/drivers/sound/configure.c
@@ -0,0 +1,500 @@
+/*
+ * sound/configure.c	- Configuration program for the Linux Sound Driver
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include <stdio.h>
+
+#define B(x)	(1 << (x))
+
+/*
+ * Option numbers
+ */
+
+#define OPT_PAS		0
+#define OPT_SB		1
+#define OPT_ADLIB	2
+#define OPT_LAST_MUTUAL	2
+
+#define OPT_GUS		3
+#define OPT_MPU401	4
+
+#define OPT_HIGHLEVEL   5
+#define OPT_SBPRO	5
+#define OPT_AUDIO	6
+#define OPT_MIDI_AUTO	7
+#define OPT_MIDI	8
+#define OPT_YM3812_AUTO	9	/* Select this automaticly if user selects
+				 * MIDI or AdLib driver */
+#define OPT_YM3812	10	/* Select this if the previous one was not
+				 * selected */
+#define OPT_SEQUENCER	11
+#define OPT_CHIP_MIDI   12	/* New support added at UW - Milwauklee UW -
+				 * Milwauklee */
+#define OPT_LAST	11
+
+#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)|B(OPT_MPU401))
+
+typedef struct
+  {
+    unsigned long   conditions;
+    unsigned long   exclusive_options;
+    char            macro[20];
+    int             verify;
+    int             alias;
+  }
+
+hw_entry;
+
+
+/*
+ * The rule table for the driver options. The first field defines a set of
+ * options which must be selected before this entry can be selected. The
+ * second field is a set of options which are not allowed with this one. If
+ * the fourth field is zero, the option is selected without asking
+ * confirmation from the user.
+ * 
+ * With this version of the rule table it is possible to select just one type of
+ * hardware.
+ * 
+ * NOTE!	Keep the following table and the questions array in sync with the
+ * option numbering!
+ */
+
+hw_entry        hw_table[] =
+{
+/* 0 */
+  {0, 0, "PAS", 1, 0},
+  {0, 0, "SB", 1, 0},
+  {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0},
+
+/* 3 */
+  {0, 0, "GUS", 1, 0},
+  {0, 0, "MPU401", 1, 0},
+  {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0},
+  {B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0},
+  {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI},
+  {B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0},
+  {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812},
+  {B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0},
+/* 10 */
+  {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0},
+  {0, 0, "CHIP_MIDI", 1, 0}
+};
+
+char           *questions[] =
+{
+  "ProAudioSpectrum 16 support",
+  "SoundBlaster support",
+  "AdLib support",
+  "Gravis Ultrasound support",
+  "MPU-401 support",
+
+  "SoundBlaster Pro support (mixer)",
+  "digitized voice support",
+  "This should not be asked",
+  "MIDI interface support",
+  "This should not be asked",
+  "Internal synthesizer (FM/GUS) support",
+  "/dev/sequencer support",
+  "MIDI on CHIP support"
+};
+
+unsigned long   selected_options = 0;
+
+int
+can_select_option (int nr)
+{
+  switch (nr)
+    {
+    case 0:
+      fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n"
+	       "cards cannot be installed at the same time\n");
+      fprintf (stderr, "\nSelect at most one of them:\n");
+      fprintf (stderr, "	- ProAudioSpectrum 16\n");
+      fprintf (stderr, "	- SoundBlaster / SB Pro\n");
+      fprintf (stderr, "          (Could be selected with PAS16 also\n"
+	       "	  since there is a SB emulation on it)\n");
+      fprintf (stderr, "	- AdLib\n");
+      fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n");
+      break;
+
+    case OPT_LAST_MUTUAL + 1:
+      fprintf (stderr, "\nThe following cards should work with any other cards.\n"
+	       "CAUTION! Don't enable MPU-401 if you don't have it.\n");
+      break;
+
+    case OPT_HIGHLEVEL:
+      fprintf (stderr, "\nSelect one or more of the following options\n");
+      break;
+
+
+    }
+
+  if (hw_table[nr].conditions)
+    if (!(hw_table[nr].conditions & selected_options))
+      return 0;
+
+  if (hw_table[nr].exclusive_options)
+    if (hw_table[nr].exclusive_options & selected_options)
+      return 0;
+
+  return 1;
+}
+
+int
+think_positively (void)
+{
+  char            answ[512];
+  int             len;
+
+  if ((len = read (0, &answ, sizeof (answ))) < 1)
+    {
+      fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+      perror ("stdin");
+      printf ("#undef CONFIGURE_SOUNDCARD\n");
+      printf ("#undef KERNEL_SOUNDCARD\n");
+      exit (-1);
+    }
+
+  if (len < 2)			/* There is an additional LF at the end */
+    return 0;
+
+  answ[len - 1] = 0;
+
+  if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
+    return 1;
+
+  return 0;
+}
+
+int
+ask_value (char *format, int default_answer)
+{
+  char            answ[512];
+  int             len, num;
+
+play_it_again_Sam:
+
+  if ((len = read (0, &answ, sizeof (answ))) < 1)
+    {
+      fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+      perror ("stdin");
+      printf ("#undef CONFIGURE_SOUNDCARD\n");
+      printf ("#undef KERNEL_SOUNDCARD\n");
+      exit (-1);
+    }
+
+  if (len < 2)			/* There is an additional LF at the end */
+    return default_answer;
+
+  answ[len - 1] = 0;
+
+  if (sscanf (answ, format, &num) != 1)
+    {
+      fprintf (stderr, "Illegal format. Try again: ");
+      goto play_it_again_Sam;
+    }
+
+  return num;
+}
+
+int
+main (int argc, char *argv[])
+{
+  int             i, num, def_size, full_driver = 1;
+  char            answ[10];
+
+  printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
+
+  fprintf (stderr, "\nConfiguring the sound support\n\n");
+
+  fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
+
+  if (think_positively ())
+    {
+      selected_options = 0xffffffff & ~B (OPT_MPU401);
+      fprintf (stderr, "Note! MPU-401 driver was not enabled\n");
+      full_driver = 1;
+    }
+  else
+    {
+      fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
+      if (think_positively ())
+	{
+	  printf ("#undef CONFIGURE_SOUNDCARD\n");
+	  printf ("#undef KERNEL_SOUNDCARD\n");
+	  exit (0);
+	}
+      /* Partial driver */
+
+      full_driver = 0;
+
+      for (i = 0; i <= OPT_LAST; i++)
+	if (can_select_option (i))
+	  {
+	    if (!(selected_options & B (i)))	/* Not selected yet */
+	      if (!hw_table[i].verify)
+		{
+		  if (hw_table[i].alias)
+		    selected_options |= B (hw_table[i].alias);
+		  else
+		    selected_options |= B (i);
+		}
+	      else
+		{
+		  fprintf (stderr, "  %s (n/y) ? ", questions[i]);
+		  if (think_positively ())
+		    if (hw_table[i].alias)
+		      selected_options |= B (hw_table[i].alias);
+		    else
+		      selected_options |= B (i);
+		}
+	  }
+    }
+
+  if (!(selected_options & ANY_DEVS))
+    {
+      printf ("#undef CONFIGURE_SOUNDCARD\n");
+      printf ("#undef KERNEL_SOUNDCARD\n");
+      fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n");
+      exit (0);
+    }
+  else
+    printf ("#define KERNEL_SOUNDCARD\n");
+
+  for (i = 0; i <= OPT_LAST; i++)
+    if (!hw_table[i].alias)
+      if (selected_options & B (i))
+	printf ("#undef  EXCLUDE_%s\n", hw_table[i].macro);
+      else
+	printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
+
+
+  printf ("#define EXCLUDE_PRO_MIDI\n");
+  printf ("#define EXCLUDE_CHIP_MIDI\n");
+
+  /*
+   * IRQ and DMA settings
+   */
+  printf ("\n");
+
+#ifdef linux
+  if (selected_options & B (OPT_SB) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+    {
+      fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
+	       "The IRQ adress is defined by the jumpers on your card and\n"
+	       "7 is the factory default. Valid values are 9, 5, 7 and 10.\n"
+	       "Enter the value: ");
+
+      num = ask_value ("%d", 7);
+      if (num != 9 && num != 5 && num != 7 && num != 10)
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 7;
+	}
+      fprintf (stderr, "SoundBlaster IRQ set to %d\n", num);
+      printf ("#define SBC_IRQ %d\n", num);
+
+      if (selected_options & B (OPT_SBPRO))
+	{
+
+	  fprintf (stderr, "\nDMA channel for SoundBlaster?\n"
+		   "For SB 1.0, 1.5 and 2.0 this MUST be 1\n"
+		   "SB Pro supports DMA channels 0, 1 and 3 (jumper)\n"
+		   "The default value is 1\n"
+		   "Enter the value: ");
+
+	  num = ask_value ("%d", 1);
+	  if (num < 0 || num > 7 || num == 4)
+	    {
+
+	      fprintf (stderr, "*** Illegal input! ***\n");
+	      num = 1;
+	    }
+	  fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
+	  printf ("#define SBC_DMA %d\n", num);
+	}
+    }
+
+  if (selected_options & B (OPT_PAS))
+    {
+      if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+	{
+	  fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n"
+		   "The recommended value is the IRQ used under DOS.\n"
+		   "Please refer to the ProAudioSpectrum User's Guide.\n"
+		   "The default value is 10.\n"
+		   "Enter the value: ");
+
+	  num = ask_value ("%d", 10);
+	  if (num == 6 || num < 3 || num > 15 || num == 2)	/* Illegal */
+	    {
+
+	      fprintf (stderr, "*** Illegal input! ***\n");
+	      num = 10;
+	    }
+	  fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num);
+	  printf ("#define PAS_IRQ %d\n", num);
+	}
+
+      if (selected_options & B (OPT_AUDIO))
+	{
+	  fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n"
+		   "The recommended value is the DMA channel under DOS.\n"
+		   "Please refer to the ProAudioSpectrum User's Guide.\n"
+		   "The default value is 3\n"
+		   "Enter the value: ");
+
+	  num = ask_value ("%d", 3);
+	  if (num == 4 || num < 0 || num > 7)
+	    {
+
+	      fprintf (stderr, "*** Illegal input! ***\n");
+	      num = 3;
+	    }
+	  fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num);
+	  printf ("#define PAS_DMA %d\n", num);
+	}
+    }
+
+  if (selected_options & B (OPT_GUS))
+    {
+      fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n"
+	       "Valid choises are 210, 220, 230, 240, 250 or 260\n"
+	       "The factory default is 220\n"
+	       "Enter the GUS I/O base: ");
+
+      num = ask_value ("%x", 0x220);
+      if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060))
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 0x220;
+	}
+
+      if ((selected_options & B (OPT_SB)) && (num == 0x220))
+	{
+	  fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n"
+		   "\t0x220 cannot be used if SoundBlaster is enabled.\n"
+		   "\tRun the config again.\n");
+	  printf ("#undef CONFIGURE_SOUNDCARD\n");
+	  printf ("#undef KERNEL_SOUNDCARD\n");
+	  exit (-1);
+	}
+      fprintf (stderr, "GUS I/O base set to %03x\n", num);
+      printf ("#define GUS_BASE 0x%03x\n", num);
+
+      fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n"
+	       "The recommended value is the IRQ used under DOS.\n"
+	       "Please refer to the Gravis Ultrasound User's Guide.\n"
+	       "The default value is 15.\n"
+	       "Enter the value: ");
+
+      num = ask_value ("%d", 15);
+      if (num == 6 || num < 3 || num > 15 || num == 2)	/* Invalid */
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 15;
+	}
+      fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num);
+      printf ("#define GUS_IRQ %d\n", num);
+
+      fprintf (stderr, "\nDMA number for Gravis UltraSound?\n"
+	       "The recommended value is the DMA channel under DOS.\n"
+	       "Please refer to the Gravis Ultrasound User's Guide.\n"
+	       "The default value is 6\n"
+	       "Enter the value: ");
+
+      num = ask_value ("%d", 6);
+      if (num == 4 || num < 0 || num > 7)
+	{
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 6;
+	}
+      fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num);
+      printf ("#define GUS_DMA %d\n", num);
+    }
+
+  if (selected_options & B (OPT_MPU401))
+    {
+      fprintf (stderr, "\nI/O base for MPU-401?\n"
+	       "The factory default is 330\n"
+	       "Enter the MPU-401 I/O base: ");
+
+      num = ask_value ("%x", 0x330);
+      fprintf (stderr, "MPU-401 I/O base set to %03x\n", num);
+      printf ("#define MPU_BASE 0x%03x\n", num);
+
+      fprintf (stderr, "\nIRQ number for MPU-401?\n"
+	       "Valid numbers are: 3, 4, 5, 7 and 9.\n"
+	       "The default value is 5.\n"
+	       "Enter the value: ");
+
+      num = ask_value ("%d", 5);
+      if (num == 6 || num < 3 || num > 15)	/* Used for floppy */
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 5;
+	}
+      fprintf (stderr, "MPU-401 IRQ set to %d\n", num);
+      printf ("#define MPU_IRQ %d\n", num);
+    }
+#endif
+
+  if (selected_options & B (OPT_AUDIO))
+    {
+      def_size = 16384;
+
+      if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS)))
+	def_size = 32768;
+      if ((selected_options & B (OPT_PAS)) && !full_driver)
+	def_size = 65536;	/* PAS16 alone */
+
+      fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
+	       "%d is recommended value for this configuration.\n"
+	       "Enter the value: ", def_size);
+
+      num = ask_value ("%d", def_size);
+      if (num != 4096 && num != 16384 && num != 32768 && num != 65536)
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = def_size;
+	}
+      fprintf (stderr, "The DMA buffer size set to %d\n", num);
+      printf ("#define DSP_BUFFSIZE %d\n", num);
+    }
+
+  printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
+  fprintf (stderr, "The sound driver is now configured.\n");
+
+  exit (0);
+}
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
new file mode 100644
index 0000000..05083d9
--- /dev/null
+++ b/drivers/sound/dev_table.c
@@ -0,0 +1,103 @@
+/*
+ * linux/kernel/chr_drv/sound/dev_table.c
+ * 
+ * Device call tables.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+long
+sndtable_init (long mem_start)
+{
+  int             i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+  for (i = 0; i < (n - 1); i++)
+    if (supported_drivers[i].probe (&supported_drivers[i].config))
+      {
+#ifndef SHORT_BANNERS
+	printk ("snd%d",
+		supported_drivers[i].card_type);
+#endif
+
+	mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
+#ifndef SHORT_BANNERS
+	printk (" at 0x%3x irq %d drq %d\n",
+		supported_drivers[i].config.io_base,
+		supported_drivers[i].config.irq,
+		supported_drivers[i].config.dma);
+#endif
+      }
+  return mem_start;
+}
+
+int
+sndtable_probe (int unit, struct address_info *hw_config)
+{
+  int             i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+  if (!unit)
+    return TRUE;
+
+  for (i = 0; i < (n - 1); i++)
+    if (supported_drivers[i].card_type == unit)
+      return supported_drivers[i].probe (hw_config);
+
+  return FALSE;
+}
+
+int
+sndtable_init_card (int unit, struct address_info *hw_config)
+{
+  int             i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+  if (!unit)
+    {
+      if (sndtable_init (0) != 0)
+	panic ("snd: Invalid memory allocation\n");
+      return TRUE;
+    }
+
+  for (i = 0; i < (n - 1); i++)
+    if (supported_drivers[i].card_type == unit)
+      {
+	if (supported_drivers[i].attach (0, hw_config) != 0)
+	  panic ("snd#: Invalid memory allocation\n");
+	return TRUE;
+      }
+
+  return FALSE;
+}
+
+int
+sndtable_get_cardcount (void)
+{
+  return num_dspdevs + num_mixers + num_synths + num_midis;
+}
+
+#endif
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
new file mode 100644
index 0000000..9bfd784
--- /dev/null
+++ b/drivers/sound/dev_table.h
@@ -0,0 +1,253 @@
+/*
+ *	dev_table.h
+ *
+ *	Global definitions for device call tables
+ * 
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+
+*/
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+/*
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ *
+ *	If you modify this file, please check the dev_table.c also.
+ *
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ */
+
+struct card_info {
+	int card_type;	/*	From soundcard.c	*/
+	char *name;
+	long (*attach) (long mem_start, struct address_info *hw_config);
+	int (*probe) (struct address_info *hw_config);
+	struct address_info config;
+};
+
+/** UWM -- new  MIDI structure here.. **/
+
+struct generic_midi_info{
+        char *name;	/* Name of the MIDI device.. */
+        long (*attach) (long mem_start);
+};
+
+struct audio_operations {
+        char name[32];
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	void (*output_block) (int dev, unsigned long buf, int count, int intrflag);
+	void (*start_input) (int dev, unsigned long buf, int count, int intrflag);
+	int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local);
+	int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+	int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+	void (*reset) (int dev);
+	void (*halt_xfer) (int dev);
+	int (*has_output_drained)(int dev);
+        void (*copy_from_user)(int dev, char *localbuf, int localoffs,
+                               snd_rw_buf *userbuf, int useroffs, int len);
+};
+
+struct mixer_operations {
+	int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+};
+
+struct synth_operations {
+	struct synth_info *info;
+	int synth_type;
+	int synth_subtype;
+
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+	int (*kill_note) (int dev, int voice, int velocity);
+	int (*start_note) (int dev, int voice, int note, int velocity);
+	int (*set_instr) (int dev, int voice, int instr);
+	void (*reset) (int dev);
+	void (*hw_control) (int dev, unsigned char *event);
+	int (*load_patch) (int dev, int format, snd_rw_buf *addr,
+	     int offs, int count, int pmgr_flag);
+	void (*aftertouch) (int dev, int voice, int pressure);
+	void (*controller) (int dev, int voice, int ctrl_num, int value);
+	void (*panning) (int dev, int voice, int value);
+	int (*pmgr_interface) (int dev, struct patmgr_info *info);
+};
+
+struct midi_operations {
+	struct midi_info info;
+	int (*open) (int dev, int mode,
+		void (*inputintr)(int dev, unsigned char data),
+		void (*outputintr)(int dev)
+		);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+	int (*putc) (int dev, unsigned char data);
+	int (*start_read) (int dev);
+	int (*end_read) (int dev);
+	void (*kick)(int dev);
+	int (*command) (int dev, unsigned char data);
+	int (*buffer_status) (int dev);
+};
+
+/** UWM -- new structure for MIDI  **/
+
+struct generic_midi_operations {
+	struct midi_info info;
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	int (*write) (int dev, snd_rw_buf *data);
+	int (*read)  (int dev, snd_rw_buf *data);
+};	
+
+#ifndef ALL_EXTERNAL_TO_ME
+
+#ifdef _MIDI_TABLE_C_
+
+/** UWM **/
+       struct generic_midi_operations * generic_midi_devs[MAX_MIDI_DEV] = {NULL}; 
+       int num_generic_midis = 0, pro_midi_dev = 0; 
+
+      struct generic_midi_info midi_supported[] = {
+
+#ifndef EXCLUDE_PRO_MIDI
+        {"ProAudioSpectrum MV101",pro_midi_attach}
+#endif
+        }; 
+
+        int num_midi_drivers = 
+            sizeof (midi_supported) / sizeof(struct generic_midi_info);
+
+#endif
+
+
+#ifdef _DEV_TABLE_C_   
+	struct audio_operations * dsp_devs[MAX_DSP_DEV] = {NULL}; int num_dspdevs = 0;
+	struct mixer_operations * mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
+	struct synth_operations * synth_devs[MAX_SYNTH_DEV] = {NULL}; int num_synths = 0;
+	struct midi_operations * midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
+
+
+#   ifndef EXCLUDE_MPU401
+        int mpu401_dev = 0;
+#   endif
+
+/*
+ *	Note! The detection order is significant. Don't change it.
+ */
+
+	struct card_info supported_drivers[] = {
+#ifndef EXCLUDE_MPU401
+		{SNDCARD_MPU401,"Roland MPU-401",	attach_mpu401, probe_mpu401,
+			{MPU_BASE, MPU_IRQ, 0}},
+#endif
+
+#ifndef EXCLUDE_GUS
+		{SNDCARD_GUS,	"Gravis Ultrasound",	attach_gus_card, probe_gus,
+			{GUS_BASE, GUS_IRQ, GUS_DMA}},
+#endif
+
+#ifndef EXCLUDE_PAS
+		{SNDCARD_PAS,	"ProAudioSpectrum",	attach_pas_card, probe_pas,
+			{PAS_BASE, PAS_IRQ, PAS_DMA}},
+#endif
+
+#ifndef EXCLUDE_SB
+		{SNDCARD_SB,	"SoundBlaster",		attach_sb_card, probe_sb,
+			{SBC_BASE, SBC_IRQ, SBC_DMA}},
+#endif
+
+#ifndef EXCLUDE_YM3812
+		{SNDCARD_ADLIB,	"AdLib",		attach_adlib_card, probe_adlib,
+			{FM_MONO, 0, 0}},
+#endif
+		{0,			"*?*",			NULL}
+	};
+
+	int num_sound_drivers =
+	    sizeof(supported_drivers) / sizeof (struct card_info);
+
+
+# ifndef EXCLUDE_AUDIO 
+	int sound_buffcounts[MAX_DSP_DEV] = {0};
+	long sound_buffsizes[MAX_DSP_DEV] = {0};
+	int sound_dsp_dmachan[MAX_DSP_DEV] = {0};
+	int sound_dma_automode[MAX_DSP_DEV] = {0};
+# endif
+#else
+	extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+	extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+	extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+	extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+#   ifndef EXCLUDE_MPU401
+        extern int mpu401_dev;
+#   endif
+
+	extern struct card_info supported_drivers[];
+	extern int num_sound_drivers;
+
+# ifndef EXCLUDE_AUDIO
+	extern int sound_buffcounts[MAX_DSP_DEV];
+	extern long sound_buffsizes[MAX_DSP_DEV];
+	extern int sound_dsp_dmachan[MAX_DSP_DEV];
+	extern int sound_dma_automode[MAX_DSP_DEV];
+# endif
+
+#endif
+
+long sndtable_init(long mem_start);
+int sndtable_get_cardcount (void);
+long CMIDI_init(long mem_start); /* */
+#endif
+
+#endif
+
+/* If external to me.... :) */
+
+#ifdef ALL_EXTERNAL_TO_ME
+
+	extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+        extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+        extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+        extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+	extern struct generic_midi_operations *generic_midi_devs[]; 
+	extern int num_generic_midis, pro_midi_dev;
+ 
+#ifndef EXCLUDE_MPU401
+        extern int mpu401_dev;
+#endif
+
+	extern struct generic_midi_info midi_supported[];
+	extern struct card_info supported_drivers[];
+        extern int num_sound_drivers;
+	extern int num_midi_drivers;	
+#ifndef EXCLUDE_AUDIO
+        extern int sound_buffcounts[MAX_DSP_DEV];
+        extern long sound_buffsizes[MAX_DSP_DEV];
+        extern int sound_dsp_dmachan[MAX_DSP_DEV];
+        extern int sound_dma_automode[MAX_DSP_DEV];
+#endif
+
+#endif
diff --git a/drivers/sound/dma.h b/drivers/sound/dma.h
new file mode 100644
index 0000000..1590815
--- /dev/null
+++ b/drivers/sound/dma.h
@@ -0,0 +1,266 @@
+/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $
+ * linux/include/asm/dma.h: Defines for using and allocating dma channels.
+ * Written by Hennus Bergman, 1992.
+ * High DMA channel support & info by Hannu Savolainen
+ * and John Boyd, Nov. 1992.
+ */
+
+#ifndef _ASM_DMA_H
+#define _ASM_DMA_H
+
+#include <asm/io.h>		/* need byte IO */
+
+#define deb_outb(x,y) {printk("out %02x, %02x\n", x, y);outb(x,y);}
+
+
+#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
+#define outb	outb_p
+#endif
+
+/*
+ * NOTES about DMA transfers:
+ *
+ *  controller 1: channels 0-3, byte operations, ports 00-1F
+ *  controller 2: channels 4-7, word operations, ports C0-DF
+ *
+ *  - ALL registers are 8 bits only, regardless of transfer size
+ *  - channel 4 is not used - cascades 1 into 2.
+ *  - channels 0-3 are byte - addresses/counts are for physical bytes
+ *  - channels 5-7 are word - addresses/counts are for physical words
+ *  - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
+ *  - transfer count loaded to registers is 1 less than actual count
+ *  - controller 2 offsets are all even (2x offsets for controller 1)
+ *  - page registers for 5-7 don't use data bit 0, represent 128K pages
+ *  - page registers for 0-3 use bit 0, represent 64K pages
+ *
+ * DMA transfers are limited to the lower 16MB of _physical_ memory.  
+ * Note that addresses loaded into registers must be _physical_ addresses,
+ * not logical addresses (which may differ if paging is active).
+ *
+ *  Address mapping for channels 0-3:
+ *
+ *   A23 ... A16 A15 ... A8  A7 ... A0    (Physical addresses)
+ *    |  ...  |   |  ... |   |  ... |
+ *    |  ...  |   |  ... |   |  ... |
+ *    |  ...  |   |  ... |   |  ... |
+ *   P7  ...  P0  A7 ... A0  A7 ... A0   
+ * |    Page    | Addr MSB | Addr LSB |   (DMA registers)
+ *
+ *  Address mapping for channels 5-7:
+ *
+ *   A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0    (Physical addresses)
+ *    |  ...  |   \   \   ... \  \  \  ... \  \
+ *    |  ...  |    \   \   ... \  \  \  ... \  (not used)
+ *    |  ...  |     \   \   ... \  \  \  ... \
+ *   P7  ...  P1 (0) A7 A6  ... A0 A7 A6 ... A0   
+ * |      Page      |  Addr MSB   |  Addr LSB  |   (DMA registers)
+ *
+ * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
+ * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
+ * the hardware level, so odd-byte transfers aren't possible).
+ *
+ * Transfer count (_not # bytes_) is limited to 64K, represented as actual
+ * count - 1 : 64K => 0xFFFF, 1 => 0x0000.  Thus, count is always 1 or more,
+ * and up to 128K bytes may be transferred on channels 5-7 in one operation. 
+ *
+ */
+
+#define MAX_DMA_CHANNELS	8
+
+/* 8237 DMA controllers */
+#define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
+#define IO_DMA2_BASE	0xC0	/* 16 bit master DMA, ch 4(=slave input)..7 */
+
+/* DMA controller registers */
+#define DMA1_CMD_REG		0x08	/* command register (w) */
+#define DMA1_STAT_REG		0x08	/* status register (r) */
+#define DMA1_REQ_REG            0x09    /* request register (w) */
+#define DMA1_MASK_REG		0x0A	/* single-channel mask (w) */
+#define DMA1_MODE_REG		0x0B	/* mode register (w) */
+#define DMA1_CLEAR_FF_REG	0x0C	/* clear pointer flip-flop (w) */
+#define DMA1_TEMP_REG           0x0D    /* Temporary Register (r) */
+#define DMA1_RESET_REG		0x0D	/* Master Clear (w) */
+#define DMA1_CLR_MASK_REG       0x0E    /* Clear Mask */
+#define DMA1_MASK_ALL_REG       0x0F    /* all-channels mask (w) */
+
+#define DMA2_CMD_REG		0xD0	/* command register (w) */
+#define DMA2_STAT_REG		0xD0	/* status register (r) */
+#define DMA2_REQ_REG            0xD2    /* request register (w) */
+#define DMA2_MASK_REG		0xD4	/* single-channel mask (w) */
+#define DMA2_MODE_REG		0xD6	/* mode register (w) */
+#define DMA2_CLEAR_FF_REG	0xD8	/* clear pointer flip-flop (w) */
+#define DMA2_TEMP_REG           0xDA    /* Temporary Register (r) */
+#define DMA2_RESET_REG		0xDA	/* Master Clear (w) */
+#define DMA2_CLR_MASK_REG       0xDC    /* Clear Mask */
+#define DMA2_MASK_ALL_REG       0xDE    /* all-channels mask (w) */
+
+#define DMA_ADDR_0              0x00    /* DMA address registers */
+#define DMA_ADDR_1              0x02
+#define DMA_ADDR_2              0x04
+#define DMA_ADDR_3              0x06
+#define DMA_ADDR_4              0xC0
+#define DMA_ADDR_5              0xC4
+#define DMA_ADDR_6              0xC8
+#define DMA_ADDR_7              0xCC
+
+#define DMA_CNT_0               0x01    /* DMA count registers */
+#define DMA_CNT_1               0x03
+#define DMA_CNT_2               0x05
+#define DMA_CNT_3               0x07
+#define DMA_CNT_4               0xC2
+#define DMA_CNT_5               0xC6
+#define DMA_CNT_6               0xCA
+#define DMA_CNT_7               0xCE
+
+#define DMA_PAGE_0              0x87    /* DMA page registers */
+#define DMA_PAGE_1              0x83
+#define DMA_PAGE_2              0x81
+#define DMA_PAGE_3              0x82
+#define DMA_PAGE_5              0x8B
+#define DMA_PAGE_6              0x89
+#define DMA_PAGE_7              0x8A
+
+#define DMA_MODE_READ	0x44	/* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE	0x48	/* memory to I/O, no autoinit, increment, single mode */
+#define DMA_MODE_CASCADE 0xC0   /* pass thru DREQ->HRQ, DACK<-HLDA only */
+
+/* enable/disable a specific DMA channel */
+static __inline__ void enable_dma(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		deb_outb(dmanr,  DMA1_MASK_REG)
+	else
+		deb_outb(dmanr & 3,  DMA2_MASK_REG);
+}
+
+static __inline__ void disable_dma(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		deb_outb(dmanr | 4,  DMA1_MASK_REG)
+	else
+		deb_outb((dmanr & 3) | 4,  DMA2_MASK_REG);
+}
+
+/* Clear the 'DMA Pointer Flip Flop'.
+ * Write 0 for LSB/MSB, 1 for MSB/LSB access.
+ * Use this once to initialize the FF to a known state.
+ * After that, keep track of it. :-)
+ * --- In order to do that, the DMA routines below should ---
+ * --- only be used while interrupts are disabled! ---
+ */
+static __inline__ void clear_dma_ff(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		deb_outb(0,  DMA1_CLEAR_FF_REG)
+	else
+		deb_outb(0,  DMA2_CLEAR_FF_REG);
+}
+
+/* set mode (above) for a specific DMA channel */
+static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
+{
+	if (dmanr<=3)
+		deb_outb(mode | dmanr,  DMA1_MODE_REG)
+	else
+		deb_outb(mode | (dmanr&3),  DMA2_MODE_REG);
+}
+
+/* Set only the page register bits of the transfer address.
+ * This is used for successive transfers when we know the contents of
+ * the lower 16 bits of the DMA current address register, but a 64k boundary
+ * may have been crossed.
+ */
+static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
+{
+	switch(dmanr) {
+		case 0:
+			deb_outb(pagenr, DMA_PAGE_0);
+			break;
+		case 1:
+			deb_outb(pagenr, DMA_PAGE_1);
+			break;
+		case 2:
+			deb_outb(pagenr, DMA_PAGE_2);
+			break;
+		case 3:
+			deb_outb(pagenr, DMA_PAGE_3);
+			break;
+		case 5:
+			deb_outb(pagenr & 0xfe, DMA_PAGE_5);
+			break;
+		case 6:
+			deb_outb(pagenr & 0xfe, DMA_PAGE_6);
+			break;
+		case 7:
+			deb_outb(pagenr & 0xfe, DMA_PAGE_7);
+			break;
+	}
+}
+
+
+/* Set transfer address & page bits for specific DMA channel.
+ * Assumes dma flipflop is clear.
+ */
+static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
+{
+	set_dma_page(dmanr, a>>16);
+	if (dmanr <= 3)  {
+	    deb_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
+            deb_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE )
+	}  else  {
+	    deb_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+	    deb_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+	}
+}
+
+
+/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
+ * a specific DMA channel.
+ * You must ensure the parameters are valid.
+ * NOTE: from a manual: "the number of transfers is one more
+ * than the initial word count"! This is taken into account.
+ * Assumes dma flip-flop is clear.
+ * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
+ */
+static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
+{
+        count--;
+	if (dmanr <= 3)  {
+	    deb_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+	    deb_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+        } else {
+	    deb_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+	    deb_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+        }
+}
+
+
+/* Get DMA residue count. After a DMA transfer, this
+ * should return zero. Reading this while a DMA transfer is
+ * still in progress will return unpredictable results.
+ * If called before the channel has been used, it may return 1.
+ * Otherwise, it returns the number of _bytes_ left to transfer.
+ *
+ * Assumes DMA flip-flop is clear.
+ */
+static __inline__ int get_dma_residue(unsigned int dmanr)
+{
+	unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
+					 : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
+
+	/* using short to get 16-bit wrap around */
+	unsigned short count;
+
+	count = 1 + inb(io_port);
+	count += inb(io_port) << 8;
+	
+	return (dmanr<=3)? count : (count<<1);
+}
+
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr);	/* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr);	/* release it again */
+
+
+#endif /* _ASM_DMA_H */
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
new file mode 100644
index 0000000..0d39eb1
--- /dev/null
+++ b/drivers/sound/dmabuf.c
@@ -0,0 +1,791 @@
+/*
+ * linux/kernel/chr_drv/sound/dmabuf.c
+ * 
+ * The DMA buffer manager for digitized voice applications
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "sound_calls.h"
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+#define MAX_SUB_BUFFERS		16
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'dma_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+#define DMODE_NONE		0
+#define DMODE_OUTPUT		1
+#define DMODE_INPUT		2
+#define DMODE_INIT		3
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);
+
+static int      dma_mode[MAX_DSP_DEV] =
+{0};				/* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+
+static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
+{0};
+
+/*
+ * Pointers to raw buffers
+ */
+
+char           *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT] =
+{
+  {NULL}};
+unsigned long   snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+int             snd_raw_count[MAX_DSP_DEV];
+
+/*
+ * Device state tables
+ */
+
+static int      dev_busy[MAX_DSP_DEV];
+static int      dev_active[MAX_DSP_DEV];
+static int      dev_qlen[MAX_DSP_DEV];
+static int      dev_qhead[MAX_DSP_DEV];
+static int      dev_qtail[MAX_DSP_DEV];
+static int      dev_underrun[MAX_DSP_DEV];
+static int      bufferalloc_done[MAX_DSP_DEV] =
+{0};
+
+/*
+ * Logical buffers for each devices
+ */
+
+static int      dev_nbufs[MAX_DSP_DEV];	/* # of logical buffers ( >=
+					 * sound_buffcounts[dev] */
+static int      dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static char    *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS] =
+{
+  {NULL}};
+static int      dev_buffsize[MAX_DSP_DEV];
+
+static void
+reorganize_buffers (int dev)
+{
+  /*
+   * This routine breaks the physical device buffers to logical ones.
+   */
+
+  unsigned long   i, p, n;
+  unsigned long   sr, nc, sz, bsz;
+
+  sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+  nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+  sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+  if (sr < 1 || nc < 1 || sz < 1)
+    {
+      printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
+      sr = DSP_DEFAULT_SPEED;
+      nc = 1;
+      sz = 8;
+    }
+
+  sz /= 8;			/* Convert # of bits -> # of bytes */
+
+  sz = sr * nc * sz;
+
+  /*
+   * Compute a buffer size not exeeding 1 second.
+   */
+
+  bsz = sound_buffsizes[dev];
+
+  while (bsz > sz)
+    bsz >>= 1;			/* Divide by 2 */
+
+  if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
+    bsz >>= 1;			/* Need at least 2 buffers */
+
+  dev_buffsize[dev] = bsz;
+  n = 0;
+
+  /*
+   * Now computing addresses for the logical buffers
+   */
+
+  for (i = 0; i < snd_raw_count[dev]; i++)
+    {
+      p = 0;
+
+      while ((p + bsz) <= sound_buffsizes[dev])
+	{
+	  dev_buf[dev][n] = snd_raw_buf[dev][i] + p;
+	  dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p;
+	  p += bsz;
+	  n++;
+	}
+    }
+
+  dev_nbufs[dev] = n;
+
+  for (i = 0; i < dev_nbufs[dev]; i++)
+    {
+      dev_counts[dev][i] = 0;
+    }
+
+  bufferalloc_done[dev] = 1;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+  int             retval;
+
+  if (dev >= num_dspdevs)
+    {
+      printk ("PCM device %d not installed.\n", dev);
+      return RET_ERROR (ENXIO);
+    }
+
+  if (dev_busy[dev])
+    return RET_ERROR (EBUSY);
+
+  if (!dsp_devs[dev])
+    {
+      printk ("DSP device %d not initialized\n", dev);
+      return RET_ERROR (ENXIO);
+    }
+
+  if (snd_raw_buf[dev][0] == NULL)
+    return RET_ERROR (ENOSPC);	/* Memory allocation failed during boot */
+
+  if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
+    return retval;
+
+  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+  dev_underrun[dev] = 0;
+
+  dev_busy[dev] = 1;
+
+  reorganize_buffers (dev);
+  bufferalloc_done[dev] = 0;
+
+  dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
+
+  return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+  dsp_devs[dev]->reset (dev);
+
+  dev_qlen[dev] = 0;
+  dev_qhead[dev] = 0;
+  dev_qtail[dev] = 0;
+  dev_active[dev] = 0;
+}
+
+static int
+dma_sync (int dev)
+{
+  unsigned long   flags;
+  unsigned long   time;
+  int             timed_out;
+
+  if (dma_mode[dev] == DMODE_OUTPUT)
+    {
+      DISABLE_INTR (flags);
+
+      timed_out = 0;
+      time = GET_TIME ();
+
+      while ((!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+		dmabuf_interrupted[dev]) && !timed_out)
+	     && dev_qlen[dev])
+	{
+	  DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+	  if ((GET_TIME () - time) > (10 * HZ))
+	    timed_out = 1;
+	}
+      RESTORE_INTR (flags);
+
+      /*
+       * Some devices such as GUS have huge amount of on board RAM for the
+       * audio data. We have to wait util the device has finished playing.
+       */
+
+      DISABLE_INTR (flags);
+      if (dsp_devs[dev]->has_output_drained)	/* Device has hidden buffers */
+	{
+	  while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+		   dmabuf_interrupted[dev])
+		 && !dsp_devs[dev]->has_output_drained (dev))
+	    {
+	      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ / 4);
+	    }
+	}
+      RESTORE_INTR (flags);
+    }
+  return dev_qlen[dev];
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+
+  if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+	dmabuf_interrupted[dev])
+      && (dma_mode[dev] == DMODE_OUTPUT))
+    {
+      dma_sync (dev);
+    }
+
+  dma_reset (dev);
+
+  if (!dev_active[dev])
+    dsp_devs[dev]->close (dev);
+
+  dma_mode[dev] = DMODE_NONE;
+  dev_busy[dev] = 0;
+
+  return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+  unsigned long   flags;
+
+  if (!bufferalloc_done[dev])
+    reorganize_buffers (dev);
+
+  if (!dma_mode[dev])
+    {
+      int             err;
+
+      if ((err = dsp_devs[dev]->prepare_for_input (dev,
+				    dev_buffsize[dev], dev_nbufs[dev])) < 0)
+	return err;
+      dma_mode[dev] = DMODE_INPUT;
+    }
+
+  if (dma_mode[dev] != DMODE_INPUT)
+    return RET_ERROR (EBUSY);	/* Can't change mode on fly */
+
+  DISABLE_INTR (flags);
+  if (!dev_qlen[dev])
+    {
+      if (!dev_active[dev])
+	{
+	  dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0);
+	  dev_active[dev] = 1;
+	}
+
+      /* Wait for the next block */
+      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+    }
+  RESTORE_INTR (flags);
+
+  if (!dev_qlen[dev])
+    return RET_ERROR (EINTR);
+
+  *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
+  *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];
+
+  return dev_qhead[dev];
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+  int             p = dev_counts[dev][dev_qhead[dev]] + c;
+
+  if (p >= dev_buffsize[dev])
+    {				/* This buffer is now empty */
+      dev_counts[dev][dev_qhead[dev]] = 0;
+      dev_qlen[dev]--;
+      dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+    }
+  else
+    dev_counts[dev][dev_qhead[dev]] = p;
+
+  return 0;
+}
+
+int
+DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
+{
+  char           *dmabuf;
+  int             buff_no, c, err;
+
+  /*
+   * This routine returns at most 'count' bytes from the dsp input buffers.
+   * Returns negative value if there is an error.
+   */
+
+  if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0)
+    return buff_no;
+
+  if (c > count)
+    c = count;
+
+  COPY_TO_USER (user_buf, 0, dmabuf, c);
+
+  if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0)
+    return err;
+  return c;
+
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+  switch (cmd)
+    {
+    case SNDCTL_DSP_RESET:
+      dma_reset (dev);
+      return 0;
+      break;
+
+    case SNDCTL_DSP_SYNC:
+      dma_sync (dev);
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETBLKSIZE:
+      if (!bufferalloc_done[dev])
+	reorganize_buffers (dev);
+
+      return IOCTL_OUT (arg, dev_buffsize[dev]);
+      break;
+
+    default:
+      return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
+    }
+
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+  unsigned long   flags;
+  int             err = EIO;
+
+  if (!bufferalloc_done[dev])
+    reorganize_buffers (dev);
+
+  if (!dma_mode[dev])
+    {
+      int             err;
+
+      dma_mode[dev] = DMODE_OUTPUT;
+      if ((err = dsp_devs[dev]->prepare_for_output (dev,
+				    dev_buffsize[dev], dev_nbufs[dev])) < 0)
+	return err;
+    }
+
+  if (dma_mode[dev] != DMODE_OUTPUT)
+    return RET_ERROR (EBUSY);	/* Can't change mode on fly */
+
+  DISABLE_INTR (flags);
+
+  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
+  if (dev_qlen[dev] == dev_nbufs[dev])
+    {
+      if (!dev_active[dev])
+	{
+	  printk ("Soundcard warning: DMA not activated %d/%d\n",
+		  dev_qlen[dev], dev_nbufs[dev]);
+	  return RET_ERROR (EIO);
+	}
+
+      /* Wait for free space */
+      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+      if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+	{
+	  printk ("Sound: DMA timed out\n");
+	  err = EIO;
+	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+	}
+      else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+	err = EINTR;
+    }
+  RESTORE_INTR (flags);
+
+  if (dev_qlen[dev] == dev_nbufs[dev])
+    return RET_ERROR (err);	/* We have got signal (?) */
+
+  *buf = dev_buf[dev][dev_qtail[dev]];
+  *size = dev_buffsize[dev];
+  dev_counts[dev][dev_qtail[dev]] = 0;
+
+  return dev_qtail[dev];
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+  if (buff_no != dev_qtail[dev])
+    printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]);
+
+  dev_qlen[dev]++;
+
+  dev_counts[dev][dev_qtail[dev]] = l;
+
+  dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+  if (!dev_active[dev])
+    {
+      dev_active[dev] = 1;
+      dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0);
+    }
+
+  return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+  int             chan = sound_dsp_dmachan[dev];
+  unsigned long   flags;
+
+  /*
+   * This function is not as portable as it should be.
+   */
+
+  /*
+   * The count must be one less than the actual size. This is handled by
+   * set_dma_addr()
+   */
+
+  if (sound_dma_automode[dev])
+    {				/* Auto restart mode. Transfer the whole
+				 * buffer */
+#ifdef linux
+      DISABLE_INTR (flags);
+      disable_dma (chan);
+      clear_dma_ff (chan);
+      set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
+      set_dma_addr (chan, snd_raw_buf_phys[dev][0]);
+      set_dma_count (chan, sound_buffsizes[dev]);
+      enable_dma (chan);
+      RESTORE_INTR (flags);
+#else
+
+#ifdef __386BSD__
+      printk ("sound: Invalid DMA mode for device %d\n", dev);
+
+      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+		    snd_raw_buf_phys[dev][0],
+		    sound_buffsizes[dev],
+		    chan);
+#else
+#if defined(ISC) || defined(SCO)
+      printk ("sound: Invalid DMA mode for device %d\n", dev);
+      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef ISC
+		 | DMAMODE_AUTO
+#endif
+		 ,
+		 snd_raw_buf_phys[dev][0], count /* - 1 ???? */ );
+      dma_enable (chan);
+#else
+#  error This routine is not valid for this OS.
+#endif
+#endif
+
+#endif
+    }
+  else
+    {
+#ifdef linux
+      DISABLE_INTR (flags);
+      disable_dma (chan);
+      clear_dma_ff (chan);
+      set_dma_mode (chan, dma_mode);
+      set_dma_addr (chan, physaddr);
+      set_dma_count (chan, count);
+      enable_dma (chan);
+      RESTORE_INTR (flags);
+#else
+#ifdef __386BSD__
+      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+		    physaddr,
+		    count,
+		    chan);
+#else
+
+#if defined(ISC) || defined(SCO)
+      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+		 physaddr, count);
+      dma_enable (chan);
+#else
+#  error This routine is not valid for this OS.
+#endif /* !ISC */
+#endif
+
+#endif
+    }
+
+  return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+  int             i;
+
+  /*
+   * In this version the DMA buffer allocation is done by sound_mem_init()
+   * which is called by init/main.c
+   */
+
+  for (i = 0; i < MAX_DSP_DEV; i++)
+    {
+      dev_qlen[i] = 0;
+      dev_qhead[i] = 0;
+      dev_qtail[i] = 0;
+      dev_active[i] = 0;
+      dev_busy[i] = 0;
+      bufferalloc_done[i] = 0;
+    }
+
+  return mem_start;
+}
+
+void
+DMAbuf_outputintr (int dev)
+{
+  unsigned long   flags;
+
+  dev_active[dev] = 0;
+  dev_qlen[dev]--;
+  dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+
+  if (dev_qlen[dev])
+    {
+      dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1);
+      dev_active[dev] = 1;
+    }
+  else
+    {
+      if (dev_busy[dev])
+	{
+	  dev_underrun[dev]++;
+	  dsp_devs[dev]->halt_xfer (dev);
+	}
+      else
+	{			/* Device has been closed */
+	  dsp_devs[dev]->close (dev);
+	}
+    }
+
+  DISABLE_INTR (flags);
+  if (SOMEONE_WAITING (dev_sleep_flag[dev]))
+    {
+      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+    }
+  RESTORE_INTR (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+  unsigned long   flags;
+
+  dev_active[dev] = 0;
+  if (!dev_busy[dev])
+    {
+      dsp_devs[dev]->close (dev);
+    }
+  else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
+    {
+      dev_underrun[dev]++;
+      dsp_devs[dev]->halt_xfer (dev);
+    }
+  else
+    {
+      dev_qlen[dev]++;
+      dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+      dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1);
+      dev_active[dev] = 1;
+    }
+
+  DISABLE_INTR (flags);
+  if (SOMEONE_WAITING (dev_sleep_flag[dev]))
+    {
+      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+    }
+  RESTORE_INTR (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+  unsigned long   flags;
+  int             chan = sound_dsp_dmachan[dev];
+
+  if (ALLOC_DMA_CHN (chan))
+    {
+      printk ("Unable to grab DMA%d for the audio driver\n", chan);
+      return 0;
+    }
+
+  DISABLE_INTR (flags);
+#ifdef linux
+  disable_dma (chan);
+  clear_dma_ff (chan);
+#endif
+  RESTORE_INTR (flags);
+
+  return 1;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+  int             chan = sound_dsp_dmachan[dev];
+
+  DMAbuf_reset_dma (chan);
+  RELEASE_DMA_CHN (chan);
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+}
+
+/*
+ * The sound_mem_init() is called by mem_init() immediately after mem_map is
+ * initialized and before free_page_list is created.
+ * 
+ * This routine allocates DMA buffers at the end of available physical memory (
+ * <16M) and marks pages reserved at mem_map.
+ */
+
+#else
+/* Stub versions if audio services not included	 */
+
+int
+DMAbuf_open (int dev, int mode)
+{
+  return RET_ERROR (ENXIO);
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+  return 0;
+}
+
+int
+DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+  return RET_ERROR (EIO);
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+  return mem_start;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_open_dma (int chan)
+{
+  return RET_ERROR (ENXIO);
+}
+
+void
+DMAbuf_close_dma (int chan)
+{
+  return;
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+  return;
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+  return;
+}
+
+void
+DMAbuf_outputintr (int dev)
+{
+  return;
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/dsp.c b/drivers/sound/dsp.c
new file mode 100644
index 0000000..fca931a
--- /dev/null
+++ b/drivers/sound/dsp.c
@@ -0,0 +1,270 @@
+/*
+ * linux/kernel/chr_drv/sound/dsp.c
+ * 
+ * Device file manager for /dev/dsp
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_AUDIO
+
+#define ON		1
+#define OFF		0
+
+static int      wr_buff_no[MAX_DSP_DEV];	/* != -1, if there is a
+						 * incomplete output block */
+static int      wr_buff_size[MAX_DSP_DEV], wr_buf_ptr[MAX_DSP_DEV];
+static char    *wr_dma_buf[MAX_DSP_DEV];
+
+int
+dsp_open (int dev, struct fileinfo *file, int bits)
+{
+  int             mode;
+  int             ret;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  if ((ret = DMAbuf_open (dev, mode)) < 0)
+    return ret;
+
+  if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits)
+    {
+      dsp_release (dev, file);
+      return RET_ERROR (ENXIO);
+    }
+
+  wr_buff_no[dev] = -1;
+
+  return ret;
+}
+
+void
+dsp_release (int dev, struct fileinfo *file)
+{
+  int             mode;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  if (wr_buff_no[dev] >= 0)
+    {
+      DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]);
+
+      wr_buff_no[dev] = -1;
+    }
+
+  DMAbuf_release (dev, mode);
+}
+
+
+int
+dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  int             c, p, l;
+  int             err;
+
+  dev = dev >> 4;
+
+  p = 0;
+  c = count;
+
+  if (!count)			/* Flush output */
+    {
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return 0;
+    }
+
+  while (c)
+    {				/* Perform output blocking */
+      if (wr_buff_no[dev] < 0)	/* There is no incomplete buffers */
+	{
+	  if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev],
+						   &wr_buff_size[dev])) < 0)
+	    return wr_buff_no[dev];
+	  wr_buf_ptr[dev] = 0;
+	}
+
+      l = c;
+      if (l > (wr_buff_size[dev] - wr_buf_ptr[dev]))
+	l = (wr_buff_size[dev] - wr_buf_ptr[dev]);
+
+      if (!dsp_devs[dev]->copy_from_user)
+	{			/* No device specific copy routine */
+	  COPY_FROM_USER (&wr_dma_buf[dev][wr_buf_ptr[dev]], buf, p, l);
+	}
+      else
+	dsp_devs[dev]->copy_from_user (dev,
+			       wr_dma_buf[dev], wr_buf_ptr[dev], buf, p, l);
+
+      c -= l;
+      p += l;
+      wr_buf_ptr[dev] += l;
+
+      if (wr_buf_ptr[dev] >= wr_buff_size[dev])
+	{
+	  if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev])) < 0)
+	    return err;
+
+	  wr_buff_no[dev] = -1;
+	}
+
+    }
+
+  return count;
+}
+
+
+int
+dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  int             c, p, l;
+  char           *dmabuf;
+  int             buff_no;
+
+  dev = dev >> 4;
+  p = 0;
+  c = count;
+
+  while (c)
+    {
+      if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
+	return buff_no;
+
+      if (l > c)
+	l = c;
+
+      /* Insert any local processing here. */
+
+      COPY_TO_USER (buf, 0, dmabuf, l);
+
+      DMAbuf_rmchars (dev, buff_no, l);
+
+      p += l;
+      c -= l;
+    }
+
+  return count - c;
+}
+
+int
+dsp_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg)
+{
+
+  dev = dev >> 4;
+
+  switch (cmd)
+    {
+    case SNDCTL_DSP_SYNC:
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+      break;
+
+    case SNDCTL_DSP_POST:
+      if (wr_buff_no[dev] >= 0)
+	{
+	  DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]);
+
+	  wr_buff_no[dev] = -1;
+	}
+      return 0;
+      break;
+
+    case SNDCTL_DSP_RESET:
+      wr_buff_no[dev] = -1;
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+      break;
+
+    default:
+      return DMAbuf_ioctl (dev, cmd, arg, 0);
+    }
+}
+
+long
+dsp_init (long mem_start)
+{
+  return mem_start;
+}
+
+#else
+/* Stub version */
+int
+dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+dsp_open (int dev, struct fileinfo *file, int bits)
+{
+  return RET_ERROR (ENXIO);
+}
+
+void
+dsp_release (int dev, struct fileinfo *file)
+  {
+  };
+int
+dsp_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+  return RET_ERROR (EIO);
+}
+
+long
+dsp_init (long mem_start)
+{
+  return mem_start;
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/finetune.h b/drivers/sound/finetune.h
new file mode 100644
index 0000000..b86a0eb
--- /dev/null
+++ b/drivers/sound/finetune.h
@@ -0,0 +1,49 @@
+#ifdef SEQUENCER_C
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+  unsigned short finetune_table[128] =
+	{
+/*   0 */  9439,  9447,  9456,  9464,  9473,  9481,  9490,  9499, 
+/*   8 */  9507,  9516,  9524,  9533,  9542,  9550,  9559,  9567, 
+/*  16 */  9576,  9585,  9593,  9602,  9611,  9619,  9628,  9637, 
+/*  24 */  9645,  9654,  9663,  9672,  9680,  9689,  9698,  9707, 
+/*  32 */  9715,  9724,  9733,  9742,  9750,  9759,  9768,  9777, 
+/*  40 */  9786,  9795,  9803,  9812,  9821,  9830,  9839,  9848, 
+/*  48 */  9857,  9866,  9874,  9883,  9892,  9901,  9910,  9919, 
+/*  56 */  9928,  9937,  9946,  9955,  9964,  9973,  9982,  9991, 
+/*  64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063, 
+/*  72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136, 
+/*  80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210, 
+/*  88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284, 
+/*  96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358, 
+/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433, 
+/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509, 
+/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585
+	};
+#else
+  extern unsigned short finetune_table[128];
+#endif
diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c
new file mode 100644
index 0000000..422ff59
--- /dev/null
+++ b/drivers/sound/gus_card.c
@@ -0,0 +1,202 @@
+/*
+ * linux/kernel/chr_drv/sound/gus_card.c
+ * 
+ * Detection routine for the Gravis Ultrasound.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include "gus_hw.h"
+
+void            gusintr (int);
+
+int             gus_base, gus_irq, gus_dma;
+
+static int
+set_gus_irq (int interrupt_level)
+{
+  int             retcode;
+
+#ifdef linux
+  struct sigaction sa;
+
+  sa.sa_handler = gusintr;
+
+#ifdef SND_SA_INTERRUPT
+  sa.sa_flags = SA_INTERRUPT;
+#else
+  sa.sa_flags = 0;
+#endif
+
+  sa.sa_mask = 0;
+  sa.sa_restorer = NULL;
+
+  retcode = irqaction (interrupt_level, &sa);
+
+  if (retcode < 0)
+    {
+      printk ("GUS: IRQ%d already in use\n", interrupt_level);
+    }
+
+#else
+  /* #  error Unimplemented for this OS	 */
+#endif
+  return retcode;
+}
+
+int
+gus_set_midi_irq (int interrupt_level)
+{
+  int             retcode;
+
+#ifdef linux
+  struct sigaction sa;
+
+  sa.sa_handler = gus_midi_interrupt;
+
+#ifdef SND_SA_INTERRUPT
+  sa.sa_flags = SA_INTERRUPT;
+#else
+  sa.sa_flags = 0;
+#endif
+
+  sa.sa_mask = 0;
+  sa.sa_restorer = NULL;
+
+  retcode = irqaction (interrupt_level, &sa);
+
+  if (retcode < 0)
+    {
+      printk ("GUS: IRQ%d already in use\n", interrupt_level);
+    }
+
+#else
+  /* #  error Unimplemented for this OS	 */
+#endif
+  return retcode;
+}
+
+long
+attach_gus_card (long mem_start, struct address_info *hw_config)
+{
+  int             io_addr;
+
+  set_gus_irq (hw_config->irq);
+
+  if (gus_wave_detect (hw_config->io_base))	/* Try first the default */
+    {
+      mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+      mem_start = gus_midi_init (mem_start);
+#endif
+      return mem_start;
+    }
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+  /*
+   * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+   */
+
+  for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+    if (io_addr != hw_config->io_base)	/* Already tested */
+      if (gus_wave_detect (io_addr))
+	{
+	  printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base);
+	  mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+	  mem_start = gus_midi_init (mem_start);
+#endif
+	  return mem_start;
+	}
+
+#endif
+
+  return mem_start;		/* Not detected */
+}
+
+int
+probe_gus (struct address_info *hw_config)
+{
+  int             io_addr;
+
+  if (gus_wave_detect (hw_config->io_base))
+    return 1;
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+  /*
+   * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+   */
+
+  for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+    if (io_addr != hw_config->io_base)	/* Already tested */
+      if (gus_wave_detect (io_addr))
+	return 1;
+
+#endif
+
+  return 0;
+}
+
+void
+gusintr (int unit)
+{
+  unsigned char   src;
+
+  while (1)
+    {
+      if (!(src = INB (u_IrqStatus)))
+	return;
+
+      if (src & DMA_TC_IRQ)
+	{
+	  guswave_dma_irq ();
+	}
+
+      if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+	{
+#ifndef EXCLUDE_MIDI
+	  gus_midi_interrupt (0);
+#endif
+	}
+
+      if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+	{
+	  printk ("T");
+	  gus_write8 (0x45, 0);	/* Timer control */
+	}
+
+      if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+	{
+	  gus_voice_irq ();
+	}
+    }
+}
+
+#endif
diff --git a/drivers/sound/gus_hw.h b/drivers/sound/gus_hw.h
new file mode 100644
index 0000000..48233e7
--- /dev/null
+++ b/drivers/sound/gus_hw.h
@@ -0,0 +1,35 @@
+
+/*
+ * I/O addresses
+ */
+
+#define u_Base			(gus_base + 0x000)
+#define u_Mixer			u_Base
+#define u_Status		(gus_base + 0x006)
+#define u_TimerControl		(gus_base + 0x008)
+#define u_TimerData		(gus_base + 0x009)
+#define u_IRQDMAControl		(gus_base + 0x00b)
+#define u_MidiControl		(gus_base + 0x100)
+#define 	MIDI_RESET		0x03
+#define		MIDI_ENABLE_XMIT	0x20
+#define		MIDI_ENABLE_RCV		0x80
+#define u_MidiStatus		u_MidiControl
+#define		MIDI_RCV_FULL		0x01
+#define 	MIDI_XMIT_EMPTY		0x02
+#define 	MIDI_FRAME_ERR		0x10
+#define 	MIDI_OVERRUN		0x20
+#define 	MIDI_IRQ_PEND		0x80
+#define u_MidiData		(gus_base + 0x101)
+#define u_Voice			(gus_base + 0x102)
+#define u_Command		(gus_base + 0x103)
+#define u_DataLo		(gus_base + 0x104)
+#define u_DataHi		(gus_base + 0x105)
+#define u_IrqStatus		u_Status
+#	define MIDI_TX_IRQ		0x01	/* pending MIDI xmit IRQ */
+#	define MIDI_RX_IRQ		0x02	/* pending MIDI recv IRQ */
+#	define GF1_TIMER1_IRQ		0x04	/* general purpose timer */
+#	define GF1_TIMER2_IRQ		0x08	/* general purpose timer */
+#	define WAVETABLE_IRQ		0x20	/* pending wavetable IRQ */
+#	define ENVELOPE_IRQ		0x40	/* pending volume envelope IRQ */
+#	define DMA_TC_IRQ		0x80	/* pending dma tc IRQ */
+#define u_DRAMIO		(gus_base + 0x107)
diff --git a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c
new file mode 100644
index 0000000..b5cd684
--- /dev/null
+++ b/drivers/sound/gus_midi.c
@@ -0,0 +1,283 @@
+/*
+ * linux/kernel/chr_drv/sound/gus2_midi.c
+ * 
+ * The low level driver for the GUS Midi Interface.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "gus_hw.h"
+
+#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI)
+
+static int      midi_busy = 0, input_opened = 0;
+static int      my_dev;
+static int      output_used = 0;
+static volatile unsigned char gus_midi_control;
+
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+extern int      gus_base, gus_irq, gus_dma;
+
+#define GUS_MIDI_STATUS()	INB(u_MidiStatus)
+
+static int
+gus_midi_open (int dev, int mode,
+	       void            (*input) (int dev, unsigned char data),
+	       void            (*output) (int dev)
+)
+{
+
+  if (midi_busy)
+    {
+      printk ("GUS: Midi busy\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  OUTB (MIDI_RESET, u_MidiControl);
+  gus_delay ();
+
+  gus_midi_control = 0;
+  input_opened = 0;
+
+  if (mode == OPEN_READ || mode == OPEN_READWRITE)
+    {
+      gus_midi_control |= MIDI_ENABLE_RCV;
+      input_opened = 1;
+    }
+
+  if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+    {
+      gus_midi_control |= MIDI_ENABLE_XMIT;
+    }
+
+  OUTB (gus_midi_control, u_MidiControl);	/* Enable */
+
+  midi_busy = 1;
+  qlen = qhead = qtail = output_used = 0;
+  midi_input_intr = input;
+
+  return 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+  unsigned long   flags;
+  int             ok = 0;
+
+  output_used = 1;
+
+  DISABLE_INTR (flags);
+
+  if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
+    {
+      ok = 1;
+      OUTB (midi_byte, u_MidiData);
+    }
+  else
+    {
+      /* Enable Midi xmit interrupts (again) */
+      gus_midi_control |= MIDI_ENABLE_XMIT;
+      OUTB (gus_midi_control, u_MidiControl);
+    }
+
+  RESTORE_INTR (flags);
+  return ok;
+}
+
+static void
+gus_midi_close (int dev)
+{
+  /* Reset FIFO pointers, disable intrs */
+
+  OUTB (MIDI_RESET, u_MidiControl);
+  midi_busy = 0;
+}
+
+static int
+gus_midi_out (int dev, unsigned char midi_byte)
+{
+
+  unsigned long   flags;
+
+  /*
+   * Drain the local queue first
+   */
+
+  DISABLE_INTR (flags);
+
+  while (qlen && dump_to_midi (tmp_queue[qhead]))
+    {
+      qlen--;
+      qhead++;
+    }
+
+  RESTORE_INTR (flags);
+
+  /*
+   * Output the byte if the local queue is empty.
+   */
+
+  if (!qlen)
+    if (dump_to_midi (midi_byte))
+      return 1;			/* OK */
+
+  /*
+   * Put to the local queue
+   */
+
+  if (qlen >= 256)
+    return 0;			/* Local queue full */
+
+  DISABLE_INTR (flags);
+
+  tmp_queue[qtail] = midi_byte;
+  qlen++;
+  qtail++;
+
+  RESTORE_INTR (flags);
+
+  return 1;
+}
+
+static int
+gus_midi_start_read (int dev)
+{
+  return 0;
+}
+
+static int
+gus_midi_end_read (int dev)
+{
+  return 0;
+}
+
+static int
+gus_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+  return RET_ERROR (EINVAL);
+}
+
+static void
+gus_midi_kick (int dev)
+{
+}
+
+static int
+gus_midi_buffer_status (int dev)
+{
+  unsigned long   flags;
+
+  if (!output_used)
+    return 0;
+
+  DISABLE_INTR (flags);
+
+  if (qlen && dump_to_midi (tmp_queue[qhead]))
+    {
+      qlen--;
+      qhead++;
+    }
+
+  RESTORE_INTR (flags);
+
+  return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
+}
+
+static struct midi_operations gus_midi_operations =
+{
+  {"Gravis UltraSound", 0},
+  gus_midi_open,
+  gus_midi_close,
+  gus_midi_ioctl,
+  gus_midi_out,
+  gus_midi_start_read,
+  gus_midi_end_read,
+  gus_midi_kick,
+  NULL,				/* command */
+  gus_midi_buffer_status
+};
+
+long
+gus_midi_init (long mem_start)
+{
+  OUTB (MIDI_RESET, u_MidiControl);
+
+  my_dev = num_midis;
+  midi_devs[num_midis++] = &gus_midi_operations;
+  return mem_start;
+}
+
+void
+gus_midi_interrupt (int dummy)
+{
+  unsigned char   stat, data;
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+
+  stat = GUS_MIDI_STATUS ();
+
+  if (stat & MIDI_RCV_FULL)
+    {
+      data = INB (u_MidiData);
+      if (input_opened)
+	midi_input_intr (my_dev, data);
+    }
+
+  if (stat & MIDI_XMIT_EMPTY)
+    {
+      while (qlen && dump_to_midi (tmp_queue[qhead]))
+	{
+	  qlen--;
+	  qhead++;
+	}
+
+      if (!qlen)
+	{
+	  /* Disable Midi output interrupts, since no data in the buffer */
+	  gus_midi_control &= ~MIDI_ENABLE_XMIT;
+	  OUTB (gus_midi_control, u_MidiControl);
+	}
+    }
+
+  if (stat & MIDI_FRAME_ERR)
+    printk ("Midi framing error\n");
+  if (stat & MIDI_OVERRUN && input_opened)
+    printk ("GUS: Midi input overrun\n");
+
+  RESTORE_INTR (flags);
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/gus_vol.c b/drivers/sound/gus_vol.c
new file mode 100644
index 0000000..b3f1e84
--- /dev/null
+++ b/drivers/sound/gus_vol.c
@@ -0,0 +1,101 @@
+/*
+ * gus_vol.c - Compute volume for GUS.
+ * 
+ * Greg Lee 1993.
+ */
+#include "sound_config.h"
+#ifndef EXCLUDE_GUS
+
+#define GUS_VOLUME	gus_wave_volume
+
+
+extern int      gus_wave_volume;
+
+/*
+ * Calculate gus volume from note velocity, main volume, expression, and
+ * intrinsic patch volume given in patch library.  Expression is multiplied
+ * in, so it emphasizes differences in note velocity, while main volume is
+ * added in -- I don't know whether this is right, but it seems reasonable to
+ * me.  (In the previous stage, main volume controller messages were changed
+ * to expression controller messages, if they were found to be used for
+ * dynamic volume adjustments, so here, main volume can be assumed to be
+ * constant throughout a song.)
+ * 
+ * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
+ * we can give a big boost to very weak voices like nylon guitar and the
+ * basses.  The normal value is 64.  Strings are assigned lower values.
+ */
+unsigned short
+gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
+{
+  int             i, m, n, x;
+
+
+  /*
+   * A voice volume of 64 is considered neutral, so adjust the main volume if
+   * something other than this neutral value was assigned in the patch
+   * library.
+   */
+  x = 256 + 6 * (voicev - 64);
+
+  /* Boost expression by voice volume above neutral. */
+  if (voicev > 65)
+    xpn += voicev - 64;
+  xpn += (voicev - 64) / 2;
+
+  /* Combine multiplicative and level components. */
+  x = vel * xpn * 6 + (voicev / 4) * x;
+
+#ifdef GUS_VOLUME
+  /*
+   * Further adjustment by installation-specific master volume control
+   * (default 50).
+   */
+  x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+  if (x < (1 << 11))
+    return (11 << 8);
+  else if (x >= 65535)
+    return ((15 << 8) | 255);
+
+  /*
+   * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit
+   * mantissa m.
+   */
+  n = x;
+  i = 7;
+  if (n < 128)
+    {
+      while (i > 0 && n < (1 << i))
+	i--;
+    }
+  else
+    while (n > 255)
+      {
+	n >>= 1;
+	i++;
+      }
+  /*
+   * Mantissa is part of linear volume not expressed in exponent.  (This is
+   * not quite like real logs -- I wonder if it's right.)
+   */
+  m = x - (1 << i);
+
+  /* Adjust mantissa to 8 bits. */
+  if (m > 0)
+    {
+      if (i > 8)
+	m >>= i - 8;
+      else if (i < 8)
+	m <<= 8 - i;
+    }
+
+  /* low volumes give occasional sour notes */
+  if (i < 11)
+    return (11 << 8);
+
+  return ((i << 8) + m);
+}
+
+#endif
diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c
new file mode 100644
index 0000000..ac654b0
--- /dev/null
+++ b/drivers/sound/gus_wave.c
@@ -0,0 +1,2548 @@
+
+/*
+ * linux/kernel/chr_drv/sound/gus_wave.c
+ * 
+ * Driver for the Gravis UltraSound wave table synth.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+/* #define GUS_LINEAR_VOLUME	 */
+
+#include "sound_config.h"
+#include "ultrasound.h"
+#include "gus_hw.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#define MAX_SAMPLE	256
+#define MAX_PATCH	256
+
+struct voice_info
+  {
+    unsigned long   orig_freq;
+    unsigned long   current_freq;
+    unsigned long   mode;
+    int             bender;
+    int             bender_range;
+    int             panning;
+    int             midi_volume;
+    unsigned int    initial_volume;
+    unsigned int    current_volume;
+    int             loop_irq_mode, loop_irq_parm;
+#define LMODE_FINISH		1
+#define LMODE_PCM		2
+#define LMODE_PCM_STOP		3
+    int             volume_irq_mode, volume_irq_parm;
+#define VMODE_HALT		1
+#define VMODE_ENVELOPE		2
+
+    int             env_phase;
+    unsigned char   env_rate[6];
+    unsigned char   env_offset[6];
+
+    /*
+     * Volume computation parameters for gus_adagio_vol()
+     */
+    int             main_vol, expression_vol, patch_vol;
+
+  };
+
+extern int      gus_base;
+extern int      gus_irq, gus_dma;
+extern char    *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int      snd_raw_count[MAX_DSP_DEV];
+static long     gus_mem_size = 0;
+static long     free_mem_ptr = 0;
+static int      gus_busy = 0;
+static int      nr_voices = 0;	/* Number of currently allowed voices */
+static int      gus_devnum = 0;
+static int      volume_base, volume_scale, volume_method;
+
+#define VOL_METHOD_ADAGIO	1
+int             gus_wave_volume = 60;	/* Master wolume for wave (0 to 100) */
+static unsigned char mix_image = 0x00;
+
+/*
+ * Current version of this_one driver doesn't allow synth and PCM functions
+ * at the same time. The active_device specifies the active driver
+ */
+static int      active_device = 0;
+
+#define GUS_DEV_WAVE		1	/* Wave table synth */
+#define GUS_DEV_PCM_DONE	2	/* PCM device, transfer done */
+#define GUS_DEV_PCM_CONTINUE	3	/* PCM device, transfer the second
+					 * chn */
+
+static int      gus_sampling_speed;
+static int      gus_sampling_channels;
+static int      gus_sampling_bits;
+
+DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+
+/*
+ * Variables and buffers for PCM output
+ */
+#define MAX_PCM_BUFFERS		32	/* Don't change */
+static int      pcm_bsize,	/* Current blocksize */
+                pcm_nblk,	/* Current # of blocks */
+                pcm_banksize;	/* # bytes allocated for channels */
+static int      pcm_datasize[MAX_PCM_BUFFERS];	/* Actual # of bytes in blk */
+static volatile int pcm_head, pcm_tail, pcm_qlen;	/* DRAM queue */
+static volatile int pcm_active;
+static int      pcm_current_dev;
+static int      pcm_current_block;
+static unsigned long pcm_current_buf;
+static int      pcm_current_count;
+static int      pcm_current_intrflag;
+
+struct voice_info voices[32];
+
+static int      freq_div_table[] =
+{
+  44100,			/* 14 */
+  41160,			/* 15 */
+  38587,			/* 16 */
+  36317,			/* 17 */
+  34300,			/* 18 */
+  32494,			/* 19 */
+  30870,			/* 20 */
+  29400,			/* 21 */
+  28063,			/* 22 */
+  26843,			/* 23 */
+  25725,			/* 24 */
+  24696,			/* 25 */
+  23746,			/* 26 */
+  22866,			/* 27 */
+  22050,			/* 28 */
+  21289,			/* 29 */
+  20580,			/* 30 */
+  19916,			/* 31 */
+  19293				/* 32 */
+};
+
+static struct patch_info samples[MAX_SAMPLE + 1];
+static long     sample_ptrs[MAX_SAMPLE + 1];
+static int      sample_map[32];
+static int      free_sample;
+
+
+static int      patch_table[MAX_PATCH];
+static int      patch_map[32];
+
+static struct synth_info gus_info =
+{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static void     gus_poke (long addr, unsigned char data);
+static void     compute_and_set_volume (int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
+static void     compute_volume (int voice, int volume);
+
+#define	INSTANT_RAMP		-1	/* Dont use ramping */
+#define FAST_RAMP		0	/* Fastest possible ramp */
+
+static void
+reset_sample_memory (void)
+{
+  int             i;
+
+  for (i = 0; i <= MAX_SAMPLE; i++)
+    sample_ptrs[i] = -1;
+  for (i = 0; i < 32; i++)
+    sample_map[i] = -1;
+  for (i = 0; i < 32; i++)
+    patch_map[i] = -1;
+
+  gus_poke (0, 0);		/* Put silence here */
+  gus_poke (1, 0);
+
+  free_mem_ptr = 2;
+  free_sample = 0;
+
+  for (i = 0; i < MAX_PATCH; i++)
+    patch_table[i] = -1;
+}
+
+void
+gus_delay (void)
+{
+  int             i;
+
+  for (i = 0; i < 7; i++)
+    INB (u_DRAMIO);
+}
+
+static void
+gus_poke (long addr, unsigned char data)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+  OUTB (0x43, u_Command);
+  OUTB (addr & 0xff, u_DataLo);
+  OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+  OUTB (0x44, u_Command);
+  OUTB ((addr >> 16) & 0xff, u_DataHi);
+  OUTB (data, u_DRAMIO);
+  RESTORE_INTR (flags);
+}
+
+static unsigned char
+gus_peek (long addr)
+{
+  unsigned long   flags;
+  unsigned char   tmp;
+
+  DISABLE_INTR (flags);
+  OUTB (0x43, u_Command);
+  OUTB (addr & 0xff, u_DataLo);
+  OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+  OUTB (0x44, u_Command);
+  OUTB ((addr >> 16) & 0xff, u_DataHi);
+  tmp = INB (u_DRAMIO);
+  RESTORE_INTR (flags);
+
+  return tmp;
+}
+
+void
+gus_write8 (int reg, unsigned char data)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+
+  OUTB (reg, u_Command);
+  OUTB (data, u_DataHi);
+
+  RESTORE_INTR (flags);
+}
+
+unsigned char
+gus_read8 (int reg)
+{
+  unsigned long   flags;
+  unsigned char   val;
+
+  DISABLE_INTR (flags);
+  OUTB (reg | 0x80, u_Command);
+  val = INB (u_DataHi);
+  RESTORE_INTR (flags);
+
+  return val;
+}
+
+unsigned char
+gus_look8 (int reg)
+{
+  unsigned long   flags;
+  unsigned char   val;
+
+  DISABLE_INTR (flags);
+  OUTB (reg, u_Command);
+  val = INB (u_DataHi);
+  RESTORE_INTR (flags);
+
+  return val;
+}
+
+void
+gus_write16 (int reg, unsigned short data)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+
+  OUTB (reg, u_Command);
+
+  OUTB (data & 0xff, u_DataLo);
+  OUTB ((data >> 8) & 0xff, u_DataHi);
+
+  RESTORE_INTR (flags);
+}
+
+unsigned short
+gus_read16 (int reg)
+{
+  unsigned long   flags;
+  unsigned char   hi, lo;
+
+  DISABLE_INTR (flags);
+
+  OUTB (reg | 0x80, u_Command);
+
+  lo = INB (u_DataLo);
+  hi = INB (u_DataHi);
+
+  RESTORE_INTR (flags);
+
+  return ((hi << 8) & 0xff00) | lo;
+}
+
+void
+gus_write_addr (int reg, unsigned long address, int is16bit)
+{
+  unsigned long   hold_address;
+
+  if (is16bit)
+    {
+      /*
+       * Special processing required for 16 bit patches
+       */
+
+      hold_address = address;
+      address = address >> 1;
+      address &= 0x0001ffffL;
+      address |= (hold_address & 0x000c0000L);
+    }
+
+  gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+  gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+}
+
+static void
+gus_select_voice (int voice)
+{
+  if (voice < 0 || voice > 31)
+    return;
+
+  OUTB (voice, u_Voice);
+}
+
+static void
+gus_select_max_voices (int nvoices)
+{
+  if (nvoices < 14)
+    nvoices = 14;
+  if (nvoices > 32)
+    nvoices = 32;
+
+  nr_voices = nvoices;
+
+  gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void
+gus_voice_on (unsigned char mode)
+{
+  gus_write8 (0x00, mode & 0xfc);
+  gus_delay ();
+  gus_write8 (0x00, mode & 0xfc);
+}
+
+static void
+gus_voice_off (void)
+{
+  gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+}
+
+static void
+gus_voice_mode (unsigned char mode)
+{
+  gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));	/* Don't start or stop
+								 * voice */
+  gus_delay ();
+  gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_voice_freq (unsigned long freq)
+{
+  unsigned long   divisor = freq_div_table[nr_voices - 14];
+  unsigned short  fc;
+
+  fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+  fc = fc << 1;
+
+  gus_write16 (0x01, fc);
+}
+
+static void
+gus_voice_volume (unsigned short vol)
+{
+  gus_write8 (0x0d, 0x03);	/* Stop ramp before setting volume */
+  gus_write16 (0x09, vol << 4);
+}
+
+static void
+gus_voice_balance (unsigned char balance)
+{
+  gus_write8 (0x0c, balance);
+}
+
+static void
+gus_ramp_range (unsigned short low, unsigned short high)
+{
+  gus_write8 (0x07, (low >> 4) & 0xff);
+  gus_write8 (0x08, (high >> 4) & 0xff);
+}
+
+static void
+gus_ramp_rate (unsigned char scale, unsigned char rate)
+{
+  gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f));
+}
+
+static void
+gus_rampon (unsigned char mode)
+{
+  gus_write8 (0x0d, mode & 0xfc);
+  gus_delay ();
+  gus_write8 (0x0d, mode & 0xfc);
+}
+
+static void
+gus_ramp_mode (unsigned char mode)
+{
+  gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));	/* Don't start or stop
+								 * ramping */
+  gus_delay ();
+  gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_rampoff (void)
+{
+  gus_write8 (0x0d, 0x03);
+}
+
+static void
+gus_voice_init (int voice)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  gus_voice_volume (0);
+  gus_write_addr (0x0a, 0, 0);	/* Set current position to 0 */
+  gus_write8 (0x00, 0x03);	/* Voice off */
+  gus_write8 (0x0d, 0x03);	/* Ramping off */
+  RESTORE_INTR (flags);
+
+  voices[voice].panning = 0;
+  voices[voice].mode = 0;
+  voices[voice].orig_freq = 20000;
+  voices[voice].current_freq = 20000;
+  voices[voice].bender = 0;
+  voices[voice].bender_range = 200;
+  voices[voice].initial_volume = 0;
+  voices[voice].current_volume = 0;
+  voices[voice].loop_irq_mode = 0;
+  voices[voice].loop_irq_parm = 0;
+  voices[voice].volume_irq_mode = 0;
+  voices[voice].volume_irq_parm = 0;
+  voices[voice].env_phase = 0;
+  voices[voice].main_vol = 127;
+  voices[voice].patch_vol = 127;
+  voices[voice].expression_vol = 127;
+}
+
+static void
+step_envelope (int voice)
+{
+  unsigned        vol, prev_vol, phase;
+  unsigned char   rate;
+
+  if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+    {
+      gus_rampoff ();
+      return;			/* Sustain */
+    }
+
+  if (voices[voice].env_phase >= 5)
+    {
+      /*
+       * Shoot the voice off
+       */
+
+      gus_voice_init (voice);
+      return;
+    }
+
+  prev_vol = voices[voice].current_volume;
+  gus_voice_volume (prev_vol);
+  phase = ++voices[voice].env_phase;
+
+  compute_volume (voice, voices[voice].midi_volume);
+
+  vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+  rate = voices[voice].env_rate[phase];
+  gus_write8 (0x06, rate);	/* Ramping rate */
+
+  voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+  if (((vol - prev_vol) / 64) == 0)	/* No significant volume change */
+    {
+      step_envelope (voice);	/* Continue with the next phase */
+      return;
+    }
+
+  if (vol > prev_vol)
+    {
+      if (vol >= (4096 - 64))
+	vol = 4096 - 65;
+      gus_ramp_range (0, vol);
+      gus_rampon (0x20);	/* Increasing, irq */
+    }
+  else
+    {
+      if (vol <= 64)
+	vol = 65;
+      gus_ramp_range (vol, 4095);
+      gus_rampon (0x60);	/* Decreasing, irq */
+    }
+  voices[voice].current_volume = vol;
+}
+
+static void
+init_envelope (int voice)
+{
+  voices[voice].env_phase = -1;
+  voices[voice].current_volume = 64;
+
+  step_envelope (voice);
+}
+
+static void
+start_release (int voice)
+{
+  if (gus_read8 (0x00) & 0x03)
+    return;			/* Voice already stopped */
+
+  voices[voice].env_phase = 2;	/* Will be incremented by step_envelope */
+
+  voices[voice].current_volume =
+    voices[voice].initial_volume =
+    gus_read16 (0x09) >> 4;	/* Get current volume */
+
+  voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+  gus_rampoff ();
+  step_envelope (voice);
+}
+
+static void
+gus_voice_fade (int voice)
+{
+  int             instr_no = sample_map[voice], is16bits;
+
+  if (instr_no < 0 || instr_no > MAX_SAMPLE)
+    {
+      gus_write8 (0x00, 0x03);	/* Hard stop */
+      return;
+    }
+
+  is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0;	/* 8 or 16 bit samples */
+
+  if (voices[voice].mode & WAVE_ENVELOPES)
+    {
+      start_release (voice);
+      return;
+    }
+
+  /*
+   * Ramp the volume down but not too quickly.
+   */
+  if ((gus_read16 (0x09) >> 4) < 100)	/* Get current volume */
+    {
+      gus_voice_off ();
+      gus_rampoff ();
+      gus_voice_init (voice);
+      return;
+    }
+
+  gus_ramp_range (65, 4095);
+  gus_ramp_rate (2, 4);
+  gus_rampon (0x40 | 0x20);	/* Down, once, irq */
+  voices[voice].volume_irq_mode = VMODE_HALT;
+}
+
+static void
+gus_reset (void)
+{
+  int             i;
+
+  gus_select_max_voices (24);
+  volume_base = 3071;
+  volume_scale = 4;
+  volume_method = VOL_METHOD_ADAGIO;
+
+  for (i = 0; i < 32; i++)
+    {
+      gus_voice_init (i);	/* Turn voice off */
+    }
+
+  INB (u_Status);		/* Touch the status register */
+
+  gus_look8 (0x41);		/* Clear any pending DMA IRQs */
+  gus_look8 (0x49);		/* Clear any pending sample IRQs */
+  gus_read8 (0x0f);		/* Clear pending IRQs */
+
+}
+
+static void
+gus_initialize (void)
+{
+  unsigned long   flags;
+  unsigned char   dma_image, irq_image, tmp;
+
+  static unsigned char gus_irq_map[16] =
+  {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7};
+
+  static unsigned char gus_dma_map[8] =
+  {0, 1, 0, 2, 0, 3, 4, 5};
+
+  DISABLE_INTR (flags);
+
+  gus_write8 (0x4c, 0);		/* Reset GF1 */
+  gus_delay ();
+  gus_delay ();
+
+  gus_write8 (0x4c, 1);		/* Release Reset */
+  gus_delay ();
+  gus_delay ();
+
+  /*
+   * Clear all interrupts
+   */
+
+  gus_write8 (0x41, 0);		/* DMA control */
+  gus_write8 (0x45, 0);		/* Timer control */
+  gus_write8 (0x49, 0);		/* Sample control */
+
+  gus_select_max_voices (24);
+
+  INB (u_Status);		/* Touch the status register */
+
+  gus_look8 (0x41);		/* Clear any pending DMA IRQs */
+  gus_look8 (0x49);		/* Clear any pending sample IRQs */
+  gus_read8 (0x0f);		/* Clear pending IRQs */
+
+  gus_reset ();			/* Resets all voices */
+
+  gus_look8 (0x41);		/* Clear any pending DMA IRQs */
+  gus_look8 (0x49);		/* Clear any pending sample IRQs */
+  gus_read8 (0x0f);		/* Clear pending IRQs */
+
+  gus_write8 (0x4c, 7);		/* Master reset | DAC enable | IRQ enable */
+
+  /*
+   * Set up for Digital ASIC
+   */
+
+  OUTB (0x05, gus_base + 0x0f);
+
+  mix_image |= 0x02;		/* Disable line out */
+  OUTB (mix_image, u_Mixer);
+
+  OUTB (0x00, u_IRQDMAControl);
+
+  OUTB (0x00, gus_base + 0x0f);
+
+  /*
+   * Now set up the DMA and IRQ interface
+   * 
+   * The GUS supports two IRQs and two DMAs.
+   * 
+   * If GUS_MIDI_IRQ is defined and if it's != GUS_IRQ, separate Midi IRQ is set
+   * up. Otherwise the same IRQ is shared by the both devices.
+   * 
+   * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+   * Adding this support requires significant changes to the dmabuf.c, dsp.c
+   * and audio.c also.
+   */
+
+  irq_image = 0;
+  tmp = gus_irq_map[gus_irq];
+  if (!tmp)
+    printk ("Warning! GUS IRQ not selected\n");
+  irq_image |= tmp;
+
+  if (GUS_MIDI_IRQ != gus_irq)
+    {				/* The midi irq was defined and != wave irq */
+      tmp = gus_irq_map[GUS_MIDI_IRQ];
+      tmp <<= 3;
+
+      if (!tmp)
+	printk ("Warning! GUS Midi IRQ not selected\n");
+      else
+	gus_set_midi_irq (GUS_MIDI_IRQ);
+
+      irq_image |= tmp;
+    }
+  else
+    irq_image |= 0x40;		/* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+
+  dma_image = 0x40;		/* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+  tmp = gus_dma_map[gus_dma];
+  if (!tmp)
+    printk ("Warning! GUS DMA not selected\n");
+  dma_image |= tmp;
+
+  /*
+   * For some reason the IRQ and DMA addresses must be written twice
+   */
+
+  /* Doing it first time */
+
+  OUTB (mix_image, u_Mixer);	/* Select DMA control */
+  OUTB (dma_image | 0x80, u_IRQDMAControl);	/* Set DMA address */
+
+  OUTB (mix_image | 0x40, u_Mixer);	/* Select IRQ control */
+  OUTB (irq_image, u_IRQDMAControl);	/* Set IRQ address */
+
+  /* Doing it second time */
+
+  OUTB (mix_image, u_Mixer);	/* Select DMA control */
+  OUTB (dma_image, u_IRQDMAControl);	/* Set DMA address */
+
+  OUTB (mix_image | 0x40, u_Mixer);	/* Select IRQ control */
+  OUTB (irq_image, u_IRQDMAControl);	/* Set IRQ address */
+
+  gus_select_voice (0);		/* This disables writes to IRQ/DMA reg */
+
+  mix_image &= ~0x02;		/* Enable line out */
+  mix_image |= 0x08;		/* Enable IRQ */
+  OUTB (mix_image, u_Mixer);	/* Turn mixer channels on */
+
+  gus_select_voice (0);		/* This disables writes to IRQ/DMA reg */
+
+  gusintr (0);			/* Serve pending interrupts */
+  RESTORE_INTR (flags);
+}
+
+int
+gus_wave_detect (int baseaddr)
+{
+  gus_base = baseaddr;
+
+  gus_write8 (0x4c, 0);		/* Reset GF1 */
+  gus_delay ();
+  gus_delay ();
+
+  gus_write8 (0x4c, 1);		/* Release Reset */
+  gus_delay ();
+  gus_delay ();
+
+  gus_poke (0x000, 0xaa);
+  gus_poke (0x100, 0x55);
+
+  if (gus_peek (0x000) != 0xaa)
+    return 0;
+  if (gus_peek (0x100) != 0x55)
+    return 0;
+
+  gus_mem_size = 0x40000;	/* 256k */
+  gus_poke (0x40000, 0xaa);
+  if (gus_peek (0x40000) != 0xaa)
+    return 1;
+
+  gus_mem_size = 0x80000;	/* 512k */
+  gus_poke (0x80000, 0xaa);
+  if (gus_peek (0x80000) != 0xaa)
+    return 1;
+
+  gus_mem_size = 0xc0000;	/* 768k */
+  gus_poke (0xc0000, 0xaa);
+  if (gus_peek (0xc0000) != 0xaa)
+    return 1;
+
+  gus_mem_size = 0x100000;	/* 1M */
+
+  return 1;
+}
+
+static int
+guswave_ioctl (int dev,
+	       unsigned int cmd, unsigned int arg)
+{
+
+  switch (cmd)
+    {
+    case SNDCTL_SYNTH_INFO:
+      gus_info.nr_voices = nr_voices;
+      IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info));
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_RESETSAMPLES:
+      reset_sample_memory ();
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_PERCMODE:
+      return 0;
+      break;
+
+    case SNDCTL_SYNTH_MEMAVL:
+      return gus_mem_size - free_mem_ptr - 32;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+}
+
+static int
+guswave_set_instr (int dev, int voice, int instr_no)
+{
+  int             sample_no;
+
+  if (instr_no < 0 || instr_no > MAX_PATCH)
+    return RET_ERROR (EINVAL);
+
+  if (voice < 0 || voice > 31)
+    return RET_ERROR (EINVAL);
+
+  sample_no = patch_table[instr_no];
+  patch_map[voice] = -1;
+
+  if (sample_no < 0)
+    {
+      printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
+      return RET_ERROR (EINVAL);/* Patch not defined */
+    }
+
+  if (sample_ptrs[sample_no] == -1)	/* Sample not loaded */
+    {
+      printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);
+      return RET_ERROR (EINVAL);
+    }
+
+  sample_map[voice] = sample_no;
+  patch_map[voice] = instr_no;
+  return 0;
+}
+
+static int
+guswave_kill_note (int dev, int voice, int velocity)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  gus_voice_fade (voice);
+  RESTORE_INTR (flags);
+
+  return 0;
+}
+
+static void
+guswave_aftertouch (int dev, int voice, int pressure)
+{
+  short           lo_limit, hi_limit;
+  unsigned long   flags;
+
+  return;			/* Currently disabled */
+
+  if (voice < 0 || voice > 31)
+    return;
+
+  if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
+    return;			/* Don't mix with envelopes */
+
+  if (pressure < 32)
+    {
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_rampoff ();
+      compute_and_set_volume (voice, 255, 0);	/* Back to original volume */
+      RESTORE_INTR (flags);
+      return;
+    }
+
+  hi_limit = voices[voice].current_volume;
+  lo_limit = hi_limit * 99 / 100;
+  if (lo_limit < 65)
+    lo_limit = 65;
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  if (hi_limit > (4095 - 65))
+    {
+      hi_limit = 4095 - 65;
+      gus_voice_volume (hi_limit);
+    }
+  gus_ramp_range (lo_limit, hi_limit);
+  gus_ramp_rate (3, 8);
+  gus_rampon (0x58);		/* Bidirectional, Down, Loop */
+  RESTORE_INTR (flags);
+}
+
+static void
+guswave_panning (int dev, int voice, int value)
+{
+  if (voice >= 0 || voice < 32)
+    voices[voice].panning = value;
+}
+
+static void
+compute_volume (int voice, int volume)
+{
+  if (volume < 128)
+    {
+      voices[voice].midi_volume = volume;
+
+      switch (volume_method)
+	{
+	case VOL_METHOD_ADAGIO:
+	  voices[voice].initial_volume =
+	    gus_adagio_vol (volume, voices[voice].main_vol,
+			    voices[voice].expression_vol,
+			    voices[voice].patch_vol);
+	  break;
+
+	default:
+	  voices[voice].initial_volume = volume_base + (volume * volume_scale);
+	}
+    }
+
+  if (voices[voice].initial_volume > 4095)
+    voices[voice].initial_volume = 4095;
+}
+
+static void
+compute_and_set_volume (int voice, int volume, int ramp_time)
+{
+  int             current, target, rate;
+
+  compute_volume (voice, volume);
+  voices[voice].current_volume = voices[voice].initial_volume;
+
+  current = gus_read16 (0x09) >> 4;
+  target = voices[voice].initial_volume;
+
+  if (ramp_time == INSTANT_RAMP)
+    {
+      gus_rampoff ();
+      gus_voice_volume (target);
+      return;
+    }
+
+  if (ramp_time == FAST_RAMP)
+    rate = 63;
+  else
+    rate = 16;
+  gus_ramp_rate (0, rate);
+
+  if ((target - current) / 64 == 0)	/* Too close */
+    {
+      gus_rampoff ();
+      gus_voice_volume (target);
+      return;
+    }
+
+  if (target > current)
+    {
+      if (target > (4095 - 65))
+	target = 4095 - 65;
+      gus_ramp_range (current, target);
+      gus_rampon (0x00);	/* Ramp up, once, no irq */
+    }
+  else
+    {
+      if (target < 65)
+	target = 65;
+
+      gus_ramp_range (target, current);
+      gus_rampon (0x40);	/* Ramp down, once, no irq */
+    }
+}
+
+static void
+dynamic_volume_change (int voice)
+{
+  unsigned char   status;
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  status = gus_read8 (0x00);	/* Voice status */
+  RESTORE_INTR (flags);
+
+  if (status & 0x03)
+    return;			/* Voice not started */
+
+  if (!(voices[voice].mode & WAVE_ENVELOPES))
+    {
+      compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+      return;
+    }
+
+  /*
+   * Voice is running and has envelopes.
+   */
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  status = gus_read8 (0x0d);	/* Ramping status */
+  RESTORE_INTR (flags);
+
+  if (status & 0x03)		/* Sustain phase? */
+    {
+      compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+      return;
+    }
+
+  if (voices[voice].env_phase < 0)
+    return;
+
+  compute_volume (voice, voices[voice].midi_volume);
+
+#if 0				/* Is this really required */
+  voices[voice].current_volume =
+    gus_read16 (0x09) >> 4;	/* Get current volume */
+
+  voices[voice].env_phase--;
+  step_envelope (voice);
+#endif
+}
+
+static void
+guswave_controller (int dev, int voice, int ctrl_num, int value)
+{
+  unsigned long   flags;
+  unsigned long   freq;
+
+  if (voice < 0 || voice > 31)
+    return;
+
+  switch (ctrl_num)
+    {
+    case CTRL_PITCH_BENDER:
+      voices[voice].bender = value;
+      freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range);
+      voices[voice].current_freq = freq;
+
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_freq (freq);
+      RESTORE_INTR (flags);
+      break;
+
+    case CTRL_PITCH_BENDER_RANGE:
+      voices[voice].bender_range = value;
+      break;
+
+    case CTRL_EXPRESSION:
+      volume_method = VOL_METHOD_ADAGIO;
+      voices[voice].expression_vol = value;
+      dynamic_volume_change (voice);
+      break;
+
+    case CTRL_MAIN_VOLUME:
+      volume_method = VOL_METHOD_ADAGIO;
+      voices[voice].main_vol = value;
+      dynamic_volume_change (voice);
+      break;
+
+    default:			/* Ignore */
+      break;
+    }
+}
+
+static int
+guswave_start_note (int dev, int voice, int note_num, int volume)
+{
+  int             sample, best_sample, best_delta, delta_freq;
+  int             is16bits, samplep, patch, pan;
+  unsigned long   note_freq, base_note, freq, flags;
+  unsigned char   mode = 0;
+
+  if (voice < 0 || voice > 31)
+    {
+      printk ("GUS: Invalid voice\n");
+      return RET_ERROR (EINVAL);
+    }
+
+  if (note_num == 255)
+    {
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	{
+	  voices[voice].midi_volume = volume;
+	  dynamic_volume_change (voice);
+	  return 0;
+	}
+
+      compute_and_set_volume (voice, volume, 1);
+      return 0;
+    }
+
+  if ((patch = patch_map[voice]) == -1)
+    {
+      return RET_ERROR (EINVAL);
+    }
+
+  if ((samplep = patch_table[patch]) == -1)
+    {
+      return RET_ERROR (EINVAL);
+    }
+
+  note_freq = note_to_freq (note_num);
+
+  /*
+   * Find a sample within a patch so that the note_freq is between low_note
+   * and high_note.
+   */
+  sample = -1;
+
+  best_sample = samplep;
+  best_delta = 1000000;
+  while (samplep >= 0 && sample == -1)
+    {
+      delta_freq = note_freq - samples[samplep].base_note;
+      if (delta_freq < 0)
+	delta_freq = -delta_freq;
+      if (delta_freq < best_delta)
+	{
+	  best_sample = samplep;
+	  best_delta = delta_freq;
+	}
+      if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note)
+	sample = samplep;
+      else
+	samplep = samples[samplep].key;	/* Follow link */
+    }
+  if (sample == -1)
+    sample = best_sample;
+
+  if (sample == -1)
+    {
+      printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
+      return 0;			/* Should play default patch ??? */
+    }
+
+  is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;	/* 8 or 16 bit samples */
+  voices[voice].mode = samples[sample].mode;
+  voices[voice].patch_vol = samples[sample].volume;
+
+  if (voices[voice].mode & WAVE_ENVELOPES)
+    {
+      int             i;
+
+      for (i = 0; i < 6; i++)
+	{
+	  voices[voice].env_rate[i] = samples[sample].env_rate[i];
+	  voices[voice].env_offset[i] = samples[sample].env_offset[i];
+	}
+    }
+
+  sample_map[voice] = sample;
+
+  base_note = samples[sample].base_note / 100;	/* To avoid overflows */
+  note_freq /= 100;
+
+  freq = samples[sample].base_freq * note_freq / base_note;
+
+  voices[voice].orig_freq = freq;
+
+  /*
+   * Since the pitch bender may have been set before playing the note, we
+   * have to calculate the bending now.
+   */
+
+  freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+  voices[voice].current_freq = freq;
+
+  pan = (samples[sample].panning + voices[voice].panning) / 32;
+  pan += 7;
+  if (pan < 0)
+    pan = 0;
+  if (pan > 15)
+    pan = 15;
+
+  if (samples[sample].mode & WAVE_16_BITS)
+    {
+      mode |= 0x04;		/* 16 bits */
+      if ((sample_ptrs[sample] >> 18) !=
+	  ((sample_ptrs[sample] + samples[sample].len) >> 18))
+	printk ("GUS: Sample address error\n");
+    }
+
+  /*************************************************************************
+ *	CAUTION!	Interrupts disabled. Don't return before enabling
+ *************************************************************************/
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+  gus_voice_off ();		/* It may still be running */
+  gus_rampoff ();
+  if (voices[voice].mode & WAVE_ENVELOPES)
+    {
+      compute_volume (voice, volume);
+      init_envelope (voice);
+    }
+  else
+    compute_and_set_volume (voice, volume, 0);
+
+  if (samples[sample].mode & WAVE_LOOP_BACK)
+    gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len, is16bits);	/* Sample start=end */
+  else
+    gus_write_addr (0x0a, sample_ptrs[sample], is16bits);	/* Sample start=begin */
+
+  if (samples[sample].mode & WAVE_LOOPING)
+    {
+      mode |= 0x08;		/* Looping on */
+
+      if (samples[sample].mode & WAVE_BIDIR_LOOP)
+	mode |= 0x10;		/* Bidirectional looping on */
+
+      if (samples[sample].mode & WAVE_LOOP_BACK)
+	{
+	  gus_write_addr (0x0a,	/* Put the current location = loop_end */
+		  sample_ptrs[sample] + samples[sample].loop_end, is16bits);
+	  mode |= 0x40;		/* Loop backwards */
+	}
+
+      gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits);	/* Loop start location */
+      gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits);	/* Loop end location */
+    }
+  else
+    {
+      mode |= 0x20;		/* Loop irq at the end */
+      voices[voice].loop_irq_mode = LMODE_FINISH;	/* Ramp it down at the
+							 * end */
+      voices[voice].loop_irq_parm = 1;
+      gus_write_addr (0x02, sample_ptrs[sample], is16bits);	/* Loop start location */
+      gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len, is16bits);	/* Loop end location */
+    }
+  gus_voice_freq (freq);
+  gus_voice_balance (pan);
+  gus_voice_on (mode);
+  RESTORE_INTR (flags);
+
+  return 0;
+}
+
+static void
+guswave_reset (int dev)
+{
+  int             i;
+
+  for (i = 0; i < 32; i++)
+    gus_voice_init (i);
+}
+
+static int
+guswave_open (int dev, int mode)
+{
+  int             err;
+
+  if (gus_busy)
+    return RET_ERROR (EBUSY);
+
+  if ((err = DMAbuf_open_dma (gus_devnum)))
+    return err;
+
+  RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+  gus_busy = 1;
+  active_device = GUS_DEV_WAVE;
+
+  gus_reset ();
+
+  return 0;
+}
+
+static void
+guswave_close (int dev)
+{
+  gus_busy = 0;
+  active_device = 0;
+  gus_reset ();
+
+  DMAbuf_close_dma (gus_devnum);
+}
+
+static int
+guswave_load_patch (int dev, int format, snd_rw_buf * addr,
+		    int offs, int count, int pmgr_flag)
+{
+  struct patch_info patch;
+  int             instr;
+  long            sizeof_patch;
+
+  unsigned long   blk_size, blk_end, left, src_offs, target;
+
+  sizeof_patch = (long) &patch.data[0] - (long) &patch;	/* Size of the header
+							 * info */
+
+  if (format != GUS_PATCH)
+    {
+      printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format);
+      return RET_ERROR (EINVAL);
+    }
+
+  if (count < sizeof_patch)
+    {
+      printk ("GUS Error: Patch header too short\n");
+      return RET_ERROR (EINVAL);
+    }
+
+  count -= sizeof_patch;
+
+  if (free_sample >= MAX_SAMPLE)
+    {
+      printk ("GUS: Sample table full\n");
+      return RET_ERROR (ENOSPC);
+    }
+
+  /*
+   * Copy the header from user space but ignore the first bytes which have
+   * been transferred already.
+   */
+
+  COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
+
+  instr = patch.instr_no;
+
+  if (instr < 0 || instr > MAX_PATCH)
+    {
+      printk ("GUS: Invalid patch number %d\n", instr);
+      return RET_ERROR (EINVAL);
+    }
+
+  if (count < patch.len)
+    {
+      printk ("GUS Warning: Patch record too short (%d<%d)\n",
+	      count, patch.len);
+      patch.len = count;
+    }
+
+  if (patch.len <= 0 || patch.len > gus_mem_size)
+    {
+      printk ("GUS: Invalid sample length %d\n", patch.len);
+      return RET_ERROR (EINVAL);
+    }
+
+  if (patch.mode & WAVE_LOOPING)
+    {
+      if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+	{
+	  printk ("GUS: Invalid loop start\n");
+	  return RET_ERROR (EINVAL);
+	}
+
+      if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+	{
+	  printk ("GUS: Invalid loop end\n");
+	  return RET_ERROR (EINVAL);
+	}
+    }
+
+  free_mem_ptr = (free_mem_ptr + 31) & ~31;	/* Alignment 32 bytes */
+
+#define GUS_BANK_SIZE (256*1024)
+
+  if (patch.mode & WAVE_16_BITS)
+    {
+      /*
+       * 16 bit samples must fit one 256k bank.
+       */
+      if (patch.len >= GUS_BANK_SIZE)
+	{
+	  printk ("GUS: Sample (16 bit) too long %d\n", patch.len);
+	  return RET_ERROR (ENOSPC);
+	}
+
+      if ((free_mem_ptr / GUS_BANK_SIZE) !=
+	  ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+	{
+	  unsigned long   tmp_mem =	/* Align to 256K*N */
+	  ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+	  if ((tmp_mem + patch.len) > gus_mem_size)
+	    return RET_ERROR (ENOSPC);
+
+	  free_mem_ptr = tmp_mem;	/* This leaves unusable memory */
+	}
+    }
+
+  if ((free_mem_ptr + patch.len) > gus_mem_size)
+    return RET_ERROR (ENOSPC);
+
+  sample_ptrs[free_sample] = free_mem_ptr;
+
+  /* Tremolo is not possible with envelopes */
+
+  if (patch.mode & WAVE_ENVELOPES)
+    patch.mode &= ~WAVE_TREMOLO;
+
+  memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
+
+  /*
+   * Link this_one sample to the list of samples for patch 'instr'.
+   */
+
+  samples[free_sample].key = patch_table[instr];
+  patch_table[instr] = free_sample;
+
+  /*
+   * Use DMA to transfer the wave data to the DRAM
+   */
+
+  left = patch.len;
+  src_offs = 0;
+  target = free_mem_ptr;
+
+  while (left)			/* Not all moved */
+    {
+      blk_size = sound_buffsizes[gus_devnum];
+      if (blk_size > left)
+	blk_size = left;
+
+      /*
+       * DMA cannot cross 256k bank boundaries. Check for that.
+       */
+      blk_end = target + blk_size;
+
+      if ((target >> 18) != (blk_end >> 18))
+	{			/* Have to split the block */
+
+	  blk_end &= ~(256 * 1024 - 1);
+	  blk_size = blk_end - target;
+	}
+
+#ifdef GUS_NO_DMA
+      /*
+       * For some reason the DMA is not possible. We have to use PIO.
+       */
+      {
+	long            i;
+	unsigned char   data;
+
+	for (i = 0; i < blk_size; i++)
+	  {
+	    GET_BYTE_FROM_USER (data, addr, sizeof_patch + i);
+	    gus_poke (target + i, data);
+	  }
+      }
+#else /* GUS_NO_DMA */
+      {
+	unsigned long   address, hold_address;
+	unsigned char   dma_command;
+
+	/*
+	 * OK, move now. First in and then out.
+	 */
+
+	COPY_FROM_USER (snd_raw_buf[gus_devnum][0],
+			addr, sizeof_patch + src_offs,
+			blk_size);
+
+	gus_write8 (0x41, 0);	/* Disable GF1 DMA */
+	DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0],
+			  blk_size, DMA_MODE_WRITE);
+
+	/*
+	 * Set the DRAM address for the wave data
+	 */
+
+	address = target;
+
+	if (sound_dsp_dmachan[gus_devnum] > 3)
+	  {
+	    hold_address = address;
+	    address = address >> 1;
+	    address &= 0x0001ffffL;
+	    address |= (hold_address & 0x000c0000L);
+	  }
+
+	gus_write16 (0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+
+	/*
+	 * Start the DMA transfer
+	 */
+
+	dma_command = 0x21;	/* IRQ enable, DMA start */
+	if (patch.mode & WAVE_UNSIGNED)
+	  dma_command |= 0x80;	/* Invert MSB */
+	if (patch.mode & WAVE_16_BITS)
+	  dma_command |= 0x40;	/* 16 bit _DATA_ */
+	if (sound_dsp_dmachan[gus_devnum] > 3)
+	  dma_command |= 0x04;	/* 16 bit DMA channel */
+
+	gus_write8 (0x41, dma_command);	/* Let's go luteet (=bugs) */
+
+	/*
+	 * Sleep here until the DRAM DMA done interrupt is served
+	 */
+	active_device = GUS_DEV_WAVE;
+
+	DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ);
+	if (TIMED_OUT (dram_sleeper, dram_sleep_flag))
+	  printk ("GUS: DMA Transfer timed out\n");
+      }
+#endif /* GUS_NO_DMA */
+
+      /*
+       * Now the next part
+       */
+
+      left -= blk_size;
+      src_offs += blk_size;
+      target += blk_size;
+
+      gus_write8 (0x41, 0);	/* Stop DMA */
+    }
+
+  free_mem_ptr += patch.len;
+
+  if (!pmgr_flag)
+    pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
+  free_sample++;
+  return 0;
+}
+
+static void
+guswave_hw_control (int dev, unsigned char *event)
+{
+  int             voice, cmd;
+  unsigned short  p1, p2;
+  unsigned long   plong, flags;
+
+  cmd = event[2];
+  voice = event[3];
+  p1 = *(unsigned short *) &event[4];
+  p2 = *(unsigned short *) &event[6];
+  plong = *(unsigned long *) &event[4];
+
+  switch (cmd)
+    {
+
+    case _GUS_NUMVOICES:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_select_max_voices (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICESAMPLE:
+      guswave_set_instr (dev, voice, p1);
+      break;
+
+    case _GUS_VOICEON:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      p1 &= ~0x20;		/* Disable intr */
+      gus_voice_on (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEOFF:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_off ();
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEFADE:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_fade (voice);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEMODE:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      p1 &= ~0x20;		/* Disable intr */
+      gus_voice_mode (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEBALA:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_balance (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEFREQ:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_freq (plong);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEVOL:
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_voice_volume (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOICEVOL2:	/* Just update the voice value */
+      voices[voice].initial_volume =
+	voices[voice].current_volume = p1;
+      break;
+
+    case _GUS_RAMPRANGE:
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	break;			/* NO-NO */
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_ramp_range (p1, p2);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_RAMPRATE:
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	break;			/* NO-NO */
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_ramp_rate (p1, p2);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_RAMPMODE:
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	break;			/* NO-NO */
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      p1 &= ~0x20;		/* Disable intr */
+      gus_ramp_mode (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_RAMPON:
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	break;			/* NO-NO */
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      p1 &= ~0x20;		/* Disable intr */
+      gus_rampon (p1);
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_RAMPOFF:
+      if (voices[voice].mode & WAVE_ENVELOPES)
+	break;			/* NO-NO */
+      DISABLE_INTR (flags);
+      gus_select_voice (voice);
+      gus_rampoff ();
+      RESTORE_INTR (flags);
+      break;
+
+    case _GUS_VOLUME_SCALE:
+      volume_base = p1;
+      volume_scale = p2;
+      break;
+
+    default:;
+    }
+}
+
+static int
+gus_sampling_set_speed (int speed)
+{
+  if (speed <= 0)
+    return gus_sampling_speed;
+
+  if (speed > 44100)
+    speed = 44100;
+
+  gus_sampling_speed = speed;
+  return speed;
+}
+
+static int
+gus_sampling_set_channels (int channels)
+{
+  if (!channels)
+    return gus_sampling_channels;
+  if (channels > 2)
+    channels = 2;
+  if (channels < 1)
+    channels = 1;
+  gus_sampling_channels = channels;
+  return channels;
+}
+
+static int
+gus_sampling_set_bits (int bits)
+{
+  if (!bits)
+    return gus_sampling_bits;
+
+  if (bits != 8 && bits != 16)
+    bits = 8;
+
+  gus_sampling_bits = bits;
+  return bits;
+}
+
+static int
+gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+  switch (cmd)
+    {
+    case SOUND_PCM_WRITE_RATE:
+      if (local)
+	return gus_sampling_set_speed (arg);
+      return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_RATE:
+      if (local)
+	return gus_sampling_speed;
+      return IOCTL_OUT (arg, gus_sampling_speed);
+      break;
+
+    case SNDCTL_DSP_STEREO:
+      if (local)
+	return gus_sampling_set_channels (arg + 1) - 1;
+      return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1);
+      break;
+
+    case SOUND_PCM_WRITE_CHANNELS:
+      return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_CHANNELS:
+      if (local)
+	return gus_sampling_channels;
+      return IOCTL_OUT (arg, gus_sampling_channels);
+      break;
+
+    case SNDCTL_DSP_SAMPLESIZE:
+      if (local)
+	return gus_sampling_set_bits (arg);
+      return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_BITS:
+      if (local)
+	return gus_sampling_bits;
+      return IOCTL_OUT (arg, gus_sampling_bits);
+
+    case SOUND_PCM_WRITE_FILTER:	/* NOT YET IMPLEMENTED */
+      return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+      break;
+
+    case SOUND_PCM_READ_FILTER:
+      return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+  return RET_ERROR (EINVAL);
+}
+
+static void
+gus_sampling_reset (int dev)
+{
+}
+
+static int
+gus_sampling_open (int dev, int mode)
+{
+#ifdef GUS_NO_DMA
+  printk ("GUS: DMA mode not enabled. Device not supported\n");
+  return RET_ERROR (ENXIO);
+#endif
+
+  if (gus_busy)
+    return RET_ERROR (EBUSY);
+
+  gus_busy = 1;
+  active_device = 0;
+
+  gus_reset ();
+  reset_sample_memory ();
+  gus_select_max_voices (14);
+
+  gus_sampling_set_bits (8);
+  gus_sampling_set_channels (1);
+  gus_sampling_set_speed (DSP_DEFAULT_SPEED);
+  pcm_active = 0;
+
+  return 0;
+}
+
+static void
+gus_sampling_close (int dev)
+{
+  gus_reset ();
+  gus_busy = 0;
+  active_device = 0;
+}
+
+static void
+play_next_pcm_block (void)
+{
+  unsigned long   flags;
+  int             speed = gus_sampling_speed;
+  int             this_one, is16bits, chn;
+  unsigned long   dram_loc;
+  unsigned char   mode[2], ramp_mode[2];
+
+  if (!pcm_qlen)
+    return;
+
+  this_one = pcm_head;
+
+  for (chn = 0; chn < gus_sampling_channels; chn++)
+    {
+      mode[chn] = 0x00;
+      ramp_mode[chn] = 0x03;	/* Ramping and rollover off */
+
+      if (chn == 0)
+	{
+	  mode[chn] |= 0x20;	/* Loop irq */
+	  voices[chn].loop_irq_mode = LMODE_PCM;
+	}
+
+      if (gus_sampling_bits != 8)
+	{
+	  is16bits = 1;
+	  mode[chn] |= 0x04;	/* 16 bit data */
+	}
+      else
+	is16bits = 0;
+
+      dram_loc = this_one * pcm_bsize;
+      dram_loc += chn * pcm_banksize;
+
+      if (this_one == (pcm_nblk - 1))	/* Last of the DRAM buffers */
+	{
+	  mode[chn] |= 0x08;	/* Enable loop */
+	  ramp_mode[chn] = 0x03;/* Disable rollover */
+	}
+      else
+	{
+	  if (chn == 0)
+	    ramp_mode[chn] = 0x04;	/* Enable rollover bit */
+	}
+
+      DISABLE_INTR (flags);
+      gus_select_voice (chn);
+      gus_voice_freq (speed);
+
+      if (gus_sampling_channels == 1)
+	gus_voice_balance (7);	/* mono */
+      else if (chn == 0)
+	gus_voice_balance (0);	/* left */
+      else
+	gus_voice_balance (15);	/* right */
+
+      if (!pcm_active)		/* Voice not started yet */
+	{
+	  /*
+	   * The playback was not started yet (or there has been a pause).
+	   * Start the voice (again) and ask for a rollover irq at the end of
+	   * this_one block. If this_one one is last of the buffers, use just
+	   * the normal loop with irq.
+	   */
+
+	  gus_voice_off ();	/* It could already be running */
+	  gus_rampoff ();
+	  gus_voice_volume (4000);
+	  gus_ramp_range (65, 4030);
+
+	  gus_write_addr (0x0a, dram_loc, is16bits);	/* Starting position */
+	  gus_write_addr (0x02, chn * pcm_banksize, is16bits);	/* Loop start location */
+
+	  if (chn != 0)
+	    gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
+			    is16bits);	/* Loop end location */
+	}
+
+      if (chn == 0)
+	gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits);	/* Loop end location */
+      else
+	mode[chn] |= 0x08;	/* Enable loop */
+
+      if (pcm_datasize[this_one] != pcm_bsize)
+	{
+	  /* Incomplete block. Possibly the last one. */
+	  if (chn == 0)
+	    {
+	      mode[chn] &= ~0x08;	/* Disable loop */
+	      mode[chn] |= 0x20;/* Enable loop IRQ */
+	      voices[0].loop_irq_mode = LMODE_PCM_STOP;
+	      ramp_mode[chn] = 0x03;	/* No rollover bit */
+	    }
+	  else
+	    {
+	      gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits);	/* Loop end location */
+	      mode[chn] &= ~0x08;	/* Disable loop */
+	    }
+	}
+
+      RESTORE_INTR (flags);
+    }
+
+  for (chn = 0; chn < gus_sampling_channels; chn++)
+    {
+      DISABLE_INTR (flags);
+      gus_select_voice (chn);
+      gus_write8 (0x0d, ramp_mode[chn]);
+      gus_voice_on (mode[chn]);
+      RESTORE_INTR (flags);
+    }
+
+  pcm_active = 1;
+}
+
+static void
+gus_transfer_output_block (int dev, unsigned long buf,
+			   int total_count, int intrflag, int chn)
+{
+  /*
+   * This routine transfers one block of audio data to the DRAM. In mono mode
+   * it's called just once. When in stereo mode, this_one routine is called
+   * once for both channels.
+   * 
+   * The left/mono channel data is transferred to the beginning of dram and the
+   * right data to the area pointed by gus_page_size.
+   */
+
+  int             this_one, count;
+  unsigned long   flags;
+  unsigned char   dma_command;
+  unsigned long   address, hold_address;
+
+  DISABLE_INTR (flags);
+
+  count = total_count / gus_sampling_channels;
+
+  if (chn == 0)
+    {
+      if (pcm_qlen >= pcm_nblk)
+	printk ("GUS Warning: PCM buffers out of sync\n");
+
+      this_one = pcm_current_block = pcm_tail;
+      pcm_qlen++;
+      pcm_tail = (pcm_tail + 1) % pcm_nblk;
+      pcm_datasize[this_one] = count;
+    }
+  else
+    this_one = pcm_current_block;
+
+  gus_write8 (0x41, 0);		/* Disable GF1 DMA */
+  DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
+
+  address = this_one * pcm_bsize;
+  address += chn * pcm_banksize;
+
+  if (sound_dsp_dmachan[dev] > 3)
+    {
+      hold_address = address;
+      address = address >> 1;
+      address &= 0x0001ffffL;
+      address |= (hold_address & 0x000c0000L);
+    }
+
+  gus_write16 (0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+
+  dma_command = 0x21;		/* IRQ enable, DMA start */
+
+  if (gus_sampling_bits != 8)
+    dma_command |= 0x40;	/* 16 bit _DATA_ */
+  else
+    dma_command |= 0x80;	/* Invert MSB */
+
+  if (sound_dsp_dmachan[dev] > 3)
+    dma_command |= 0x04;	/* 16 bit DMA channel */
+
+  gus_write8 (0x41, dma_command);	/* Kick on */
+
+  if (chn == (gus_sampling_channels - 1))	/* Last channel */
+    {
+      /* Last (right or mono) channel data */
+      active_device = GUS_DEV_PCM_DONE;
+      if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize))
+	{
+	  play_next_pcm_block ();
+	}
+    }
+  else				/* Left channel data. The right channel is
+				 * transferred after DMA interrupt */
+    active_device = GUS_DEV_PCM_CONTINUE;
+
+  RESTORE_INTR (flags);
+}
+
+static void
+gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intrflag)
+{
+  pcm_current_buf = buf;
+  pcm_current_count = total_count;
+  pcm_current_intrflag = intrflag;
+  pcm_current_dev = dev;
+  gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
+}
+
+static void
+gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag)
+{
+  unsigned long   flags;
+  unsigned char   mode;
+
+  DISABLE_INTR (flags);
+
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+  mode = 0xa0;			/* DMA IRQ enable, invert MSB */
+
+  if (sound_dsp_dmachan[dev] > 3)
+    mode |= 0x04;		/* 16 bit DMA channel */
+  if (gus_sampling_channels > 1)
+    mode |= 0x02;		/* Stereo */
+  mode |= 0x01;			/* DMA enable */
+
+  gus_write8 (0x49, mode);
+
+  RESTORE_INTR (flags);
+}
+
+static int
+gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
+{
+  unsigned int    rate;
+
+  rate = (9878400 / (gus_sampling_speed + 2)) / 16;
+
+  gus_write8 (0x48, rate & 0xff);	/* Set sampling frequency */
+
+  if (gus_sampling_bits != 8)
+    {
+      printk ("GUS Error: 16 bit recording not supported\n");
+      return RET_ERROR (EINVAL);
+    }
+
+  return 0;
+}
+
+static int
+gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
+{
+  int             i;
+
+  long            mem_ptr, mem_size;
+
+  mem_ptr = 0;
+  mem_size = gus_mem_size / gus_sampling_channels;
+
+  if (mem_size > (256 * 1024))
+    mem_size = 256 * 1024;
+
+  pcm_bsize = bsize / gus_sampling_channels;
+  pcm_head = pcm_tail = pcm_qlen = 0;
+
+  pcm_nblk = MAX_PCM_BUFFERS;
+  if ((pcm_bsize * pcm_nblk) > mem_size)
+    pcm_nblk = mem_size / pcm_bsize;
+
+  for (i = 0; i < pcm_nblk; i++)
+    pcm_datasize[i] = 0;
+
+  pcm_banksize = pcm_nblk * pcm_bsize;
+
+  if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
+    pcm_nblk--;
+
+  return 0;
+}
+
+static int
+gus_has_output_drained (int dev)
+{
+  return !pcm_qlen;
+}
+
+static void
+gus_copy_from_user (int dev, char *localbuf, int localoffs,
+		    snd_rw_buf * userbuf, int useroffs, int len)
+{
+  if (gus_sampling_channels == 1)
+    {
+      COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
+    }
+  else if (gus_sampling_bits == 8)
+    {
+      int             in_left = useroffs;
+      int             in_right = useroffs + 1;
+      char           *out_left, *out_right;
+      int             i;
+
+      len /= 2;
+      localoffs /= 2;
+      out_left = &localbuf[localoffs];
+      out_right = out_left + pcm_bsize;
+
+      for (i = 0; i < len; i++)
+	{
+	  GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
+	  in_left += 2;
+	  GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
+	  in_right += 2;
+	}
+    }
+  else
+    {
+      int             in_left = useroffs;
+      int             in_right = useroffs + 1;
+      short          *out_left, *out_right;
+      int             i;
+
+      len /= 4;
+      localoffs /= 4;
+
+      out_left = (short *) &localbuf[localoffs];
+      out_right = out_left + (pcm_bsize / 2);
+
+      for (i = 0; i < len; i++)
+	{
+	  GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
+	  in_left += 2;
+	  GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
+	  in_right += 2;
+	}
+    }
+}
+
+static struct audio_operations gus_sampling_operations =
+{
+  "Gravis UltraSound",
+  gus_sampling_open,		/* */
+  gus_sampling_close,		/* */
+  gus_sampling_output_block,	/* */
+  gus_sampling_start_input,	/* */
+  gus_sampling_ioctl,		/* */
+  gus_sampling_prepare_for_input,	/* */
+  gus_sampling_prepare_for_output,	/* */
+  gus_sampling_reset,		/* */
+  gus_sampling_reset,		/* halt_xfer */
+  gus_has_output_drained,
+  gus_copy_from_user
+};
+
+static int
+guswave_patchmgr (int dev, struct patmgr_info *rec)
+{
+  int             i, n;
+
+  switch (rec->command)
+    {
+    case PM_GET_DEVTYPE:
+      rec->parm1 = PMTYPE_WAVE;
+      return 0;
+      break;
+
+    case PM_GET_NRPGM:
+      rec->parm1 = MAX_PATCH;
+      return 0;
+      break;
+
+    case PM_GET_PGMMAP:
+      rec->parm1 = MAX_PATCH;
+
+      for (i = 0; i < MAX_PATCH; i++)
+	{
+	  int             ptr = patch_table[i];
+
+	  rec->data.data8[i] = 0;
+
+	  while (ptr >= 0 && ptr < free_sample)
+	    {
+	      rec->data.data8[i]++;
+	      ptr = samples[ptr].key;	/* Follow link */
+	    }
+	}
+      return 0;
+      break;
+
+    case PM_GET_PGM_PATCHES:
+      {
+	int             ptr = patch_table[rec->parm1];
+
+	n = 0;
+
+	while (ptr >= 0 && ptr < free_sample)
+	  {
+	    rec->data.data32[n++] = ptr;
+	    ptr = samples[ptr].key;	/* Follow link */
+	  }
+      }
+      rec->parm1 = n;
+      return 0;
+      break;
+
+    case PM_GET_PATCH:
+      {
+	int             ptr = rec->parm1;
+	struct patch_info *pat;
+
+	if (ptr < 0 || ptr >= free_sample)
+	  return RET_ERROR (EINVAL);
+
+	memcpy (rec->data.data8, (char *) &samples[ptr],
+		sizeof (struct patch_info));
+
+	pat = (struct patch_info *) rec->data.data8;
+
+	pat->key = GUS_PATCH;	/* Restore patch type */
+	rec->parm1 = sample_ptrs[ptr];	/* DRAM address */
+	rec->parm2 = sizeof (struct patch_info);
+      }
+      return 0;
+      break;
+
+    case PM_SET_PATCH:
+      {
+	int             ptr = rec->parm1;
+	struct patch_info *pat;
+
+	if (ptr < 0 || ptr >= free_sample)
+	  return RET_ERROR (EINVAL);
+
+	pat = (struct patch_info *) rec->data.data8;
+
+	if (pat->len > samples[ptr].len)	/* Cannot expand sample */
+	  return RET_ERROR (EINVAL);
+
+	pat->key = samples[ptr].key;	/* Ensure the link is correct */
+
+	memcpy ((char *) &samples[ptr], rec->data.data8,
+		sizeof (struct patch_info));
+
+	pat->key = GUS_PATCH;
+      }
+      return 0;
+      break;
+
+    case PM_READ_PATCH:	/* Returns a block of wave data from the DRAM */
+      {
+	int             sample = rec->parm1;
+	int             n;
+	long            offs = rec->parm2;
+	int             l = rec->parm3;
+
+	if (sample < 0 || sample >= free_sample)
+	  return RET_ERROR (EINVAL);
+
+	if (offs < 0 || offs >= samples[sample].len)
+	  return RET_ERROR (EINVAL);	/* Invalid offset */
+
+	n = samples[sample].len - offs;	/* Nr of bytes left */
+
+	if (l > n)
+	  l = n;
+
+	if (l > sizeof (rec->data.data8))
+	  l = sizeof (rec->data.data8);
+
+	if (l <= 0)
+	  return RET_ERROR (EINVAL);	/* Was there a bug? */
+
+	offs += sample_ptrs[sample];	/* Begin offsess + offset to DRAM */
+
+	for (n = 0; n < l; n++)
+	  rec->data.data8[n] = gus_peek (offs++);
+	rec->parm1 = n;		/* Nr of bytes copied */
+      }
+      return 0;
+      break;
+
+    case PM_WRITE_PATCH:	/* Writes a block of wave data to the DRAM */
+      {
+	int             sample = rec->parm1;
+	int             n;
+	long            offs = rec->parm2;
+	int             l = rec->parm3;
+
+	if (sample < 0 || sample >= free_sample)
+	  return RET_ERROR (EINVAL);
+
+	if (offs < 0 || offs >= samples[sample].len)
+	  return RET_ERROR (EINVAL);	/* Invalid offset */
+
+	n = samples[sample].len - offs;	/* Nr of bytes left */
+
+	if (l > n)
+	  l = n;
+
+	if (l > sizeof (rec->data.data8))
+	  l = sizeof (rec->data.data8);
+
+	if (l <= 0)
+	  return RET_ERROR (EINVAL);	/* Was there a bug? */
+
+	offs += sample_ptrs[sample];	/* Begin offsess + offset to DRAM */
+
+	for (n = 0; n < l; n++)
+	  gus_poke (offs++, rec->data.data8[n]);
+	rec->parm1 = n;		/* Nr of bytes copied */
+      }
+      return 0;
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+}
+
+static struct synth_operations guswave_operations =
+{
+  &gus_info,
+  SYNTH_TYPE_SAMPLE,
+  SAMPLE_TYPE_GUS,
+  guswave_open,
+  guswave_close,
+  guswave_ioctl,
+  guswave_kill_note,
+  guswave_start_note,
+  guswave_set_instr,
+  guswave_reset,
+  guswave_hw_control,
+  guswave_load_patch,
+  guswave_aftertouch,
+  guswave_controller,
+  guswave_panning,
+  guswave_patchmgr
+};
+
+long
+gus_wave_init (long mem_start, int irq, int dma)
+{
+  printk (" <Gravis UltraSound %dk>", gus_mem_size / 1024);
+
+  if (irq < 0 || irq > 15)
+    {
+      printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+      return mem_start;
+    }
+
+  if (dma < 0 || dma > 7)
+    {
+      printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
+      return mem_start;
+    }
+
+  gus_irq = irq;
+  gus_dma = dma;
+
+  if (num_synths >= MAX_SYNTH_DEV)
+    printk ("GUS Error: Too many synthesizers\n");
+  else
+    synth_devs[num_synths++] = &guswave_operations;
+
+  reset_sample_memory ();
+
+  gus_initialize ();
+
+  if (num_dspdevs < MAX_DSP_DEV)
+    {
+      dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations;
+      sound_dsp_dmachan[gus_devnum] = dma;
+      sound_buffcounts[gus_devnum] = 1;
+      sound_buffsizes[gus_devnum] = DSP_BUFFSIZE;
+      sound_dma_automode[gus_devnum] = 0;
+    }
+  else
+    printk ("GUS: Too many PCM devices available\n");
+
+  return mem_start;
+}
+
+static void
+do_loop_irq (int voice)
+{
+  unsigned char   tmp;
+  int             mode, parm;
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+  gus_select_voice (voice);
+
+  tmp = gus_read8 (0x00);
+  tmp &= ~0x20;			/* Disable wave IRQ for this_one voice */
+  gus_write8 (0x00, tmp);
+
+  mode = voices[voice].loop_irq_mode;
+  voices[voice].loop_irq_mode = 0;
+  parm = voices[voice].loop_irq_parm;
+
+  switch (mode)
+    {
+
+    case LMODE_FINISH:		/* Final loop finished, shoot volume down */
+
+      if ((gus_read16 (0x09) >> 4) < 100)	/* Get current volume */
+	{
+	  gus_voice_off ();
+	  gus_rampoff ();
+	  gus_voice_init (voice);
+	  return;
+	}
+      gus_ramp_range (65, 4065);
+      gus_ramp_rate (0, 63);	/* Fastest possible rate */
+      gus_rampon (0x20 | 0x40);	/* Ramp down, once, irq */
+      voices[voice].volume_irq_mode = VMODE_HALT;
+      break;
+
+    case LMODE_PCM_STOP:
+      pcm_active = 0;		/* Requires extensive processing */
+    case LMODE_PCM:
+      {
+	int             orig_qlen = pcm_qlen;
+
+	pcm_qlen--;
+	pcm_head = (pcm_head + 1) % pcm_nblk;
+	if (pcm_qlen)
+	  {
+	    play_next_pcm_block ();
+	  }
+	else
+	  {			/* Out of data. Just stop the voice */
+	    gus_voice_off ();
+	    gus_rampoff ();
+	    pcm_active = 0;
+	  }
+
+	if (orig_qlen == pcm_nblk)
+	  {
+	    DMAbuf_outputintr (gus_devnum);
+	  }
+      }
+      break;
+
+    default:;
+    }
+  RESTORE_INTR (flags);
+}
+
+static void
+do_volume_irq (int voice)
+{
+  unsigned char   tmp;
+  int             mode, parm;
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+
+  gus_select_voice (voice);
+
+  tmp = gus_read8 (0x0d);
+  tmp &= ~0x20;			/* Disable volume ramp IRQ */
+  gus_write8 (0x0d, tmp);
+
+  mode = voices[voice].volume_irq_mode;
+  voices[voice].volume_irq_mode = 0;
+  parm = voices[voice].volume_irq_parm;
+
+  switch (mode)
+    {
+    case VMODE_HALT:		/* Decay phase finished */
+      gus_voice_init (voice);
+      break;
+
+    case VMODE_ENVELOPE:
+      gus_rampoff ();
+      step_envelope (voice);
+      break;
+
+    default:;
+    }
+
+  RESTORE_INTR (flags);
+}
+
+void
+gus_voice_irq (void)
+{
+  unsigned long   wave_ignore = 0, volume_ignore = 0;
+  unsigned long   voice_bit;
+
+  unsigned char   src, voice;
+
+  while (1)
+    {
+      src = gus_read8 (0x0f);	/* Get source info */
+      voice = src & 0x1f;
+      src &= 0xc0;
+
+      if (src == (0x80 | 0x40))
+	return;			/* No interrupt */
+
+      voice_bit = 1 << voice;
+
+      if (!(src & 0x80))	/* Wave IRQ pending */
+	if (!(wave_ignore & voice_bit) && voice < nr_voices)	/* Not done yet */
+	  {
+	    wave_ignore |= voice_bit;
+	    do_loop_irq (voice);
+	  }
+
+      if (!(src & 0x40))	/* Volume IRQ pending */
+	if (!(volume_ignore & voice_bit) && voice < nr_voices)	/* Not done yet */
+	  {
+	    volume_ignore |= voice_bit;
+	    do_volume_irq (voice);
+	  }
+    }
+}
+
+void
+guswave_dma_irq (void)
+{
+  unsigned char   status;
+
+  status = gus_look8 (0x41);	/* Get DMA IRQ Status */
+  if (status & 0x40)		/* DMA Irq pending */
+    switch (active_device)
+      {
+      case GUS_DEV_WAVE:
+	if (SOMEONE_WAITING (dram_sleep_flag))
+	  WAKE_UP (dram_sleeper, dram_sleep_flag);
+	break;
+
+      case GUS_DEV_PCM_CONTINUE:
+	gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
+				   pcm_current_count,
+				   pcm_current_intrflag, 1);
+	break;
+
+      case GUS_DEV_PCM_DONE:
+	if (pcm_qlen < pcm_nblk)
+	  {
+	    DMAbuf_outputintr (gus_devnum);
+	  }
+	break;
+
+      default:;
+      }
+
+  status = gus_look8 (0x49);	/* Get Sampling IRQ Status */
+  if (status & 0x40)		/* Sampling Irq pending */
+    {
+      DMAbuf_inputintr (gus_devnum);
+    }
+
+}
+
+#endif
diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c
new file mode 100644
index 0000000..0196f84
--- /dev/null
+++ b/drivers/sound/midibuf.c
@@ -0,0 +1,123 @@
+/*
+ * linux/kernel/chr_drv/sound/midibuf.c
+ * 
+ * Device file manager for /dev/midi
+ * 
+ * NOTE! This part of the driver is currently just a stub.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401)
+
+#if 0
+#include "midiioctl.h"
+#include "midivar.h"
+#endif
+
+static int      midibuf_busy = 0;
+
+int
+MIDIbuf_open (int dev, struct fileinfo *file)
+{
+  int             mode, err;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  if (midibuf_busy)
+    return RET_ERROR (EBUSY);
+
+  if (!mpu401_dev)
+    {
+      printk ("Midi: MPU-401 compatible Midi interface not present\n");
+      return RET_ERROR (ENXIO);
+    }
+
+  if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode, NULL, NULL)) < 0)
+    return err;
+
+  midibuf_busy = 1;
+
+  return RET_ERROR (ENXIO);
+}
+
+void
+MIDIbuf_release (int dev, struct fileinfo *file)
+{
+  int             mode;
+
+  dev = dev >> 4;
+  mode = file->mode & O_ACCMODE;
+
+  midi_devs[mpu401_dev]->close (mpu401_dev);
+  midibuf_busy = 0;
+}
+
+int
+MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+  dev = dev >> 4;
+
+  return count;
+}
+
+
+int
+MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  dev = dev >> 4;
+
+  return RET_ERROR (EIO);
+}
+
+int
+MIDIbuf_ioctl (int dev, struct fileinfo *file,
+	       unsigned int cmd, unsigned int arg)
+{
+  dev = dev >> 4;
+
+  switch (cmd)
+    {
+
+    default:
+      return midi_devs[0]->ioctl (dev, cmd, arg);
+    }
+}
+
+void
+MIDIbuf_bytes_received (int dev, unsigned char *buf, int count)
+{
+}
+
+long
+MIDIbuf_init (long mem_start)
+{
+  return mem_start;
+}
+
+#endif
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
new file mode 100644
index 0000000..a23684f
--- /dev/null
+++ b/drivers/sound/mpu401.c
@@ -0,0 +1,355 @@
+/*
+ * linux/kernel/chr_drv/sound/mpu401.c
+ * 
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ * 
+ * This version supports just the DUMB UART mode.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+
+#define	DATAPORT   (mpu401_base)/* MPU-401 Data I/O Port on IBM */
+#define	COMDPORT   (mpu401_base+1)	/* MPU-401 Command Port on IBM */
+#define	STATPORT   (mpu401_base+1)	/* MPU-401 Status Port on IBM */
+
+#define mpu401_status()		INB(STATPORT)
+#define input_avail()		(!(mpu401_status()&INPUT_AVAIL))
+#define output_ready()		(!(mpu401_status()&OUTPUT_READY))
+#define mpu401_cmd(cmd)		OUTB(cmd, COMDPORT)
+#define mpu401_read()		INB(DATAPORT)
+#define mpu401_write(byte)	OUTB(byte, DATAPORT)
+
+#define	OUTPUT_READY	0x40	/* Mask for Data Read Redy Bit */
+#define	INPUT_AVAIL	0x80	/* Mask for Data Send Ready Bit */
+#define	MPU_ACK		0xFE	/* MPU-401 Acknowledge Response */
+#define	MPU_RESET	0xFF	/* MPU-401 Total Reset Command */
+#define	UART_MODE_ON	0x3F	/* MPU-401 "Dumb UART Mode" */
+
+static int      mpu401_opened = 0;
+static int      mpu401_base = 0x330;
+static int      mpu401_irq;
+static int      mpu401_detected = 0;
+static int      my_dev;
+
+static int      reset_mpu401 (void);
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+mpu401_input_loop (void)
+{
+  int             count;
+
+  count = 10;
+
+  while (count)			/* Not timed out */
+    if (input_avail ())
+      {
+	unsigned char   c = mpu401_read ();
+
+	count = 100;
+
+	if (mpu401_opened & OPEN_READ)
+	  midi_input_intr (my_dev, c);
+      }
+    else
+      while (!input_avail () && count)
+	count--;
+}
+
+void
+mpuintr (int unit)
+{
+  if (input_avail ())
+    mpu401_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_mpu401 (unsigned long dummy)
+{
+  unsigned long   flags;
+
+  static struct timer_list mpu401_timer =
+  {NULL, 0, 0, poll_mpu401};
+
+  if (!(mpu401_opened & OPEN_READ))
+    return;			/* No longer required */
+
+  DISABLE_INTR (flags);
+
+  if (input_avail ())
+    mpu401_input_loop ();
+
+  mpu401_timer.expires = 1;
+  add_timer (&mpu401_timer);	/* Come back later */
+
+  RESTORE_INTR (flags);
+}
+
+static int
+set_mpu401_irq (int interrupt_level)
+{
+  int             retcode;
+
+#ifdef linux
+  struct sigaction sa;
+
+  sa.sa_handler = mpuintr;
+
+#ifdef SND_SA_INTERRUPT
+  sa.sa_flags = SA_INTERRUPT;
+#else
+  sa.sa_flags = 0;
+#endif
+
+  sa.sa_mask = 0;
+  sa.sa_restorer = NULL;
+
+  retcode = irqaction (interrupt_level, &sa);
+
+  if (retcode < 0)
+    {
+      printk ("MPU-401: IRQ%d already in use\n", interrupt_level);
+    }
+
+#else
+  /* #  error Unimplemented for this OS	 */
+#endif
+  return retcode;
+}
+
+static int
+mpu401_open (int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+)
+{
+  if (mpu401_opened)
+    {
+      printk ("MPU-401: Midi busy\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  mpu401_input_loop ();
+
+  midi_input_intr = input;
+  mpu401_opened = mode;
+  poll_mpu401 (0);		/* Enable input polling */
+
+  return 0;
+}
+
+static void
+mpu401_close (int dev)
+{
+  mpu401_opened = 0;
+}
+
+static int
+mpu401_out (int dev, unsigned char midi_byte)
+{
+  int             timeout;
+  unsigned long   flags;
+
+  /*
+   * Test for input since pending input seems to block the output.
+   */
+
+  DISABLE_INTR (flags);
+
+  if (input_avail ())
+    mpu401_input_loop ();
+
+  RESTORE_INTR (flags);
+
+  /*
+   * Sometimes it takes about 13000 loops before the output becomes ready
+   * (After reset). Normally it takes just about 10 loops.
+   */
+
+  for (timeout = 30000; timeout > 0 && !output_ready (); timeout--);	/* Wait */
+
+  if (!output_ready ())
+    {
+      printk ("MPU-401: Timeout\n");
+      return 0;
+    }
+
+  mpu401_write (midi_byte);
+  return 1;
+}
+
+static int
+mpu401_command (int dev, unsigned char midi_byte)
+{
+  return 1;
+}
+
+static int
+mpu401_start_read (int dev)
+{
+  return 0;
+}
+
+static int
+mpu401_end_read (int dev)
+{
+  return 0;
+}
+
+static int
+mpu401_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+  return RET_ERROR (EINVAL);
+}
+
+static void
+mpu401_kick (int dev)
+{
+}
+
+static int
+mpu401_buffer_status (int dev)
+{
+  return 0;			/* No data in buffers */
+}
+
+static struct midi_operations mpu401_operations =
+{
+  {"MPU-401", 0},
+  mpu401_open,
+  mpu401_close,
+  mpu401_ioctl,
+  mpu401_out,
+  mpu401_start_read,
+  mpu401_end_read,
+  mpu401_kick,
+  mpu401_command,
+  mpu401_buffer_status
+};
+
+
+long
+attach_mpu401 (long mem_start, struct address_info *hw_config)
+{
+  int             ok, timeout;
+  unsigned long   flags;
+
+  mpu401_base = hw_config->io_base;
+  mpu401_irq = hw_config->irq;
+
+  if (!mpu401_detected)
+    return RET_ERROR (EIO);
+
+  DISABLE_INTR (flags);
+  for (timeout = 30000; timeout < 0 && !output_ready (); timeout--);	/* Wait */
+  mpu401_cmd (UART_MODE_ON);
+
+  ok = 0;
+  for (timeout = 50000; timeout > 0 && !ok; timeout--)
+    if (input_avail ())
+      if (mpu401_read () == MPU_ACK)
+	ok = 1;
+
+  RESTORE_INTR (flags);
+
+  printk (" <Roland MPU-401>");
+
+  my_dev = num_midis;
+  mpu401_dev = num_midis;
+  midi_devs[num_midis++] = &mpu401_operations;
+  return mem_start;
+}
+
+static int
+reset_mpu401 (void)
+{
+  unsigned long   flags;
+  int             ok, timeout, n;
+
+  /*
+   * Send the RESET command. Try twice if no success at the first time.
+   */
+
+  ok = 0;
+
+  DISABLE_INTR (flags);
+
+  for (n = 0; n < 2 && !ok; n++)
+    {
+      for (timeout = 30000; timeout < 0 && !output_ready (); timeout--);	/* Wait */
+      mpu401_cmd (MPU_RESET);	/* Send MPU-401 RESET Command */
+
+      /*
+       * Wait at least 25 msec. This method is not accurate so let's make the
+       * loop bit longer. Cannot sleep since this is called during boot.
+       */
+
+      for (timeout = 50000; timeout > 0 && !ok; timeout--)
+	if (input_avail ())
+	  if (mpu401_read () == MPU_ACK)
+	    ok = 1;
+
+    }
+
+  mpu401_opened = 0;
+  if (ok)
+    mpu401_input_loop ();	/* Flush input before enabling interrupts */
+
+  RESTORE_INTR (flags);
+
+  return ok;
+}
+
+
+int
+probe_mpu401 (struct address_info *hw_config)
+{
+  int             ok = 0;
+
+  mpu401_base = hw_config->io_base;
+  mpu401_irq = hw_config->irq;
+
+  if (set_mpu401_irq (mpu401_irq) < 0)
+    return 0;
+
+  ok = reset_mpu401 ();
+
+  mpu401_detected = ok;
+  return ok;
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c
new file mode 100644
index 0000000..3a70f8b
--- /dev/null
+++ b/drivers/sound/opl3.c
@@ -0,0 +1,941 @@
+/*
+ * linux/kernel/chr_drv/sound/opl3.c
+ * 
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+/* Major improvements to the FM handling 30AUG92 by Rob Hooft, */
+/* hooft@chem.ruu.nl */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+#include "opl3.h"
+
+#define MAX_VOICE	18
+#define OFFS_4OP	11	/* Definitions for the operators OP3 and OP4
+				 * begin here */
+
+static int      opl3_enabled = 0;
+static int      left_address = 0x388, right_address = 0x388, both_address = 0;
+
+static int      nr_voices = 9;
+static int      logical_voices[MAX_VOICE] =
+{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+
+struct voice_info
+  {
+    unsigned char   keyon_byte;
+    long            bender;
+    long            bender_range;
+    unsigned long   orig_freq;
+    unsigned long   current_freq;
+    int             mode;
+  };
+
+static struct voice_info voices[MAX_VOICE];
+
+typedef struct sbi_instrument instr_array[SBFM_MAXINSTR];
+static instr_array instrmap;
+static struct sbi_instrument *active_instrument[MAX_VOICE] =
+{NULL};
+
+static struct synth_info fm_info =
+{"AdLib", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0};
+
+static int      already_initialized = 0;
+
+static int      opl3_ok = 0;
+static int      opl3_busy = 0;
+static int      fm_model = 0;	/* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2	 */
+
+static int      store_instr (int instr_no, struct sbi_instrument *instr);
+static void     freq_to_fnum (int freq, int *block, int *fnum);
+static void     opl3_command (int io_addr, const unsigned char addr, const unsigned char val);
+static int      opl3_kill_note (int dev, int voice, int velocity);
+static unsigned char connection_mask = 0x00;
+
+void
+enable_opl3_mode (int left, int right, int both)
+{
+  opl3_enabled = 1;
+  left_address = left;
+  right_address = right;
+  both_address = both;
+  fm_info.capabilities = SYNTH_CAP_OPL3;
+  fm_info.synth_subtype = FM_TYPE_OPL3;
+}
+
+static void
+enter_4op_mode (void)
+{
+  int             i;
+  static int      voices_4op[MAX_VOICE] =
+  {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
+
+  connection_mask = 0x3f;
+  opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f);	/* Select all 4-OP
+									 * voices */
+  for (i = 0; i < 3; i++)
+    physical_voices[i].voice_mode = 4;
+  for (i = 3; i < 6; i++)
+    physical_voices[i].voice_mode = 0;
+
+  for (i = 9; i < 12; i++)
+    physical_voices[i].voice_mode = 4;
+  for (i = 12; i < 15; i++)
+    physical_voices[i].voice_mode = 0;
+
+  for (i = 0; i < 12; i++)
+    logical_voices[i] = voices_4op[i];
+  nr_voices = 12;
+}
+
+static int
+opl3_ioctl (int dev,
+	    unsigned int cmd, unsigned int arg)
+{
+  switch (cmd)
+    {
+
+    case SNDCTL_FM_LOAD_INSTR:
+      {
+	struct sbi_instrument ins;
+
+	IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins));
+
+	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+	  {
+	    printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+	    return RET_ERROR (EINVAL);
+	  }
+
+	pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
+	return store_instr (ins.channel, &ins);
+      }
+      break;
+
+    case SNDCTL_SYNTH_INFO:
+      fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices;
+
+      IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
+      return 0;
+      break;
+
+    case SNDCTL_SYNTH_MEMAVL:
+      return 0x7fffffff;
+      break;
+
+    case SNDCTL_FM_4OP_ENABLE:
+      if (opl3_enabled)
+	enter_4op_mode ();
+      return 0;
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+}
+
+int
+opl3_detect (int ioaddr)
+{
+  /*
+   * This function returns 1 if the FM chicp is present at the given I/O port
+   * The detection algorithm plays with the timer built in the FM chip and
+   * looks for a change in the status register.
+   * 
+   * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+   * boards.
+   * 
+   * Note2! The chip is initialized if detected.
+   */
+
+  unsigned char   stat1, stat2;
+  int             i;
+
+  if (already_initialized)
+    {
+      return 0;			/* Do avoid duplicate initializations */
+    }
+
+  if (opl3_enabled)
+    ioaddr = left_address;
+
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);	/* Reset timers 1 and 2 */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);	/* Reset the IRQ of FM
+								 * chicp */
+
+  stat1 = INB (ioaddr);		/* Read status register */
+
+  if ((stat1 & 0xE0) != 0x00)
+    {
+      return 0;			/* Should be 0x00	 */
+    }
+
+  opl3_command (ioaddr, TIMER1_REGISTER, 0xff);	/* Set timer 1 to 0xff */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
+		TIMER2_MASK | TIMER1_START);	/* Unmask and start timer 1 */
+
+  /*
+   * Now we have to delay at least 80 msec
+   */
+
+  for (i = 0; i < 50; i++)
+    tenmicrosec ();		/* To be sure */
+
+  stat2 = INB (ioaddr);		/* Read status after timers have expired */
+
+  /* Stop the timers */
+
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);	/* Reset timers 1 and 2 */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);	/* Reset the IRQ of FM
+								 * chicp */
+
+  if ((stat2 & 0xE0) != 0xc0)
+    {
+      return 0;			/* There is no YM3812 */
+    }
+
+  /* There is a FM chicp in this address. Now set some default values. */
+
+  for (i = 0; i < 9; i++)
+    opl3_command (ioaddr, KEYON_BLOCK + i, 0);	/* Note off */
+
+  opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+  opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00);	/* Melodic mode. */
+
+  return 1;
+}
+
+static int
+opl3_kill_note (int dev, int voice, int velocity)
+{
+  struct physical_voice_info *map;
+
+  if (voice < 0 || voice >= nr_voices)
+    return 0;
+
+  map = &physical_voices[logical_voices[voice]];
+
+  DEB (printk ("Kill note %d\n", voice));
+
+  if (map->voice_mode == 0)
+    return 0;
+
+  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20);
+
+  voices[voice].keyon_byte = 0;
+  voices[voice].bender = 0;
+  voices[voice].bender_range = 200;	/* 200 cents = 2 semitones */
+  voices[voice].orig_freq = 0;
+  voices[voice].current_freq = 0;
+  voices[voice].mode = 0;
+
+  return 0;
+}
+
+#define HIHAT			0
+#define CYMBAL			1
+#define TOMTOM			2
+#define SNARE			3
+#define BDRUM			4
+#define UNDEFINED		TOMTOM
+#define DEFAULT			TOMTOM
+
+static int
+store_instr (int instr_no, struct sbi_instrument *instr)
+{
+
+  if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
+    printk ("FM warning: Invalid patch format field (key) 0x%04x\n", instr->key);
+  memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
+
+  return 0;
+}
+
+static int
+opl3_set_instr (int dev, int voice, int instr_no)
+{
+  if (voice < 0 || voice >= nr_voices)
+    return 0;
+
+  if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+    return 0;
+
+  active_instrument[voice] = &instrmap[instr_no];
+  return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
+ * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+char            fm_volume_table[128] =
+{-64, -48, -40, -35, -32, -29, -27, -26,	/* 0 -   7 */
+ -24, -23, -21, -20, -19, -18, -18, -17,	/* 8 -  15 */
+ -16, -15, -15, -14, -13, -13, -12, -12,	/* 16 -  23 */
+ -11, -11, -10, -10, -10, -9, -9, -8,	/* 24 -  31 */
+ -8, -8, -7, -7, -7, -6, -6, -6,/* 32 -  39 */
+ -5, -5, -5, -5, -4, -4, -4, -4,/* 40 -  47 */
+ -3, -3, -3, -3, -2, -2, -2, -2,/* 48 -  55 */
+ -2, -1, -1, -1, -1, 0, 0, 0,	/* 56 -  63 */
+ 0, 0, 0, 1, 1, 1, 1, 1,	/* 64 -  71 */
+ 1, 2, 2, 2, 2, 2, 2, 2,	/* 72 -  79 */
+ 3, 3, 3, 3, 3, 3, 3, 4,	/* 80 -  87 */
+ 4, 4, 4, 4, 4, 4, 4, 5,	/* 88 -  95 */
+ 5, 5, 5, 5, 5, 5, 5, 5,	/* 96 - 103 */
+ 6, 6, 6, 6, 6, 6, 6, 6,	/* 104 - 111 */
+ 6, 7, 7, 7, 7, 7, 7, 7,	/* 112 - 119 */
+ 7, 7, 7, 8, 8, 8, 8, 8};	/* 120 - 127 */
+
+static void
+calc_vol (unsigned char *regbyte, int volume)
+{
+  int             level = (~*regbyte & 0x3f);
+
+  if (level)
+    level += fm_volume_table[volume];
+
+  if (level > 0x3f)
+    level = 0x3f;
+  if (level < 0)
+    level = 0;
+
+  *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void
+set_voice_volume (int voice, int volume)
+{
+  unsigned char   vol1, vol2, vol3, vol4;
+  struct sbi_instrument *instr;
+  struct physical_voice_info *map;
+
+  if (voice < 0 || voice >= nr_voices)
+    return;
+
+  map = &physical_voices[logical_voices[voice]];
+
+  instr = active_instrument[voice];
+
+  if (!instr)
+    instr = &instrmap[0];
+
+  if (instr->channel < 0)
+    return;
+
+  if (voices[voice].mode == 0)
+    return;
+
+  if (voices[voice].mode == 2)
+    {				/* 2 OP voice */
+
+      vol1 = instr->operators[2];
+      vol2 = instr->operators[3];
+
+      if ((instr->operators[10] & 0x01))
+	{			/* Additive synthesis	 */
+	  calc_vol (&vol1, volume);
+	  calc_vol (&vol2, volume);
+	}
+      else
+	{			/* FM synthesis */
+	  calc_vol (&vol2, volume);
+	}
+
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);	/* Modulator volume */
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);	/* Carrier volume */
+    }
+  else
+    {				/* 4 OP voice */
+      int             connection;
+
+      vol1 = instr->operators[2];
+      vol2 = instr->operators[3];
+      vol3 = instr->operators[OFFS_4OP + 2];
+      vol4 = instr->operators[OFFS_4OP + 3];
+
+      /*
+       * The connection method for 4 OP voices is defined by the rightmost
+       * bits at the offsets 10 and 10+OFFS_4OP
+       */
+
+      connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+      switch (connection)
+	{
+	case 0:
+	  calc_vol (&vol4, volume);	/* Just the OP 4 is carrier */
+	  break;
+
+	case 1:
+	  calc_vol (&vol2, volume);
+	  calc_vol (&vol4, volume);
+	  break;
+
+	case 2:
+	  calc_vol (&vol1, volume);
+	  calc_vol (&vol4, volume);
+	  break;
+
+	case 3:
+	  calc_vol (&vol1, volume);
+	  calc_vol (&vol3, volume);
+	  calc_vol (&vol4, volume);
+	  break;
+
+	default:/* Why ?? */ ;
+	}
+
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+    }
+}
+
+static int
+opl3_start_note (int dev, int voice, int note, int volume)
+{
+  unsigned char   data, fpc;
+  int             block, fnum, freq, voice_mode;
+  struct sbi_instrument *instr;
+  struct physical_voice_info *map;
+
+  if (voice < 0 || voice >= nr_voices)
+    return 0;
+
+  map = &physical_voices[logical_voices[voice]];
+
+  if (map->voice_mode == 0)
+    return 0;
+
+  if (note == 255)		/* Just change the volume */
+    {
+      set_voice_volume (voice, volume);
+      return 0;
+    }
+
+  /* Kill previous note before playing */
+  opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff);	/* Carrier volume to min */
+  opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff);	/* Modulator volume to */
+
+  if (map->voice_mode == 4)
+    {
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+    }
+
+  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00);	/* Note off */
+
+  instr = active_instrument[voice];
+
+  if (!instr)
+    instr = &instrmap[0];
+
+  if (instr->channel < 0)
+    {
+      printk (
+	       "OPL3: Initializing voice %d with undefined instrument\n",
+	       voice);
+      return 0;
+    }
+
+  if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+    return 0;			/* Cannot play */
+
+  voice_mode = map->voice_mode;
+
+  if (voice_mode == 4)
+    {
+      int             voice_shift;
+
+      voice_shift = (map->ioaddr == left_address) ? 0 : 3;
+      voice_shift += map->voice_num;
+
+      if (instr->key != OPL3_PATCH)	/* Just 2 OP patch */
+	{
+	  voice_mode = 2;
+	  connection_mask &= ~(1 << voice_shift);
+	}
+      else
+	{
+	  connection_mask |= (1 << voice_shift);
+	}
+
+      opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+    }
+
+  /* Set Sound Characteristics */
+  opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+  opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+  /* Set Attack/Decay */
+  opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+  opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+  /* Set Sustain/Release */
+  opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+  opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+  /* Set Wave Select */
+  opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+  opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+  /* Set Feedback/Connection */
+  fpc = instr->operators[10];
+  if (!(fpc & 0x30))
+    fpc |= 0x30;		/* Ensure that at least one chn is enabled */
+  opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
+		fpc);
+
+  /*
+   * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+   */
+
+  if (voice_mode == 4)
+    {
+
+      /* Set Sound Characteristics */
+      opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+      opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+      /* Set Attack/Decay */
+      opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+      opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+      /* Set Sustain/Release */
+      opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+      opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+      /* Set Wave Select */
+      opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+      opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+      /* Set Feedback/Connection */
+      fpc = instr->operators[OFFS_4OP + 10];
+      if (!(fpc & 0x30))
+	fpc |= 0x30;		/* Ensure that at least one chn is enabled */
+      opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+    }
+
+  voices[voice].mode = voice_mode;
+
+  set_voice_volume (voice, volume);
+
+  freq = voices[voice].orig_freq = note_to_freq (note) / 1000;
+
+  /*
+   * Since the pitch bender may have been set before playing the note, we
+   * have to calculate the bending now.
+   */
+
+  freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+  voices[voice].current_freq = freq;
+
+  freq_to_fnum (freq, &block, &fnum);
+
+  /* Play note */
+
+  data = fnum & 0xff;		/* Least significant bits of fnumber */
+  opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+  data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+  voices[voice].keyon_byte = data;
+  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+  if (voice_mode == 4)
+    opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+  return 0;
+}
+
+static void
+freq_to_fnum (int freq, int *block, int *fnum)
+{
+  int             f, octave;
+
+  /* Converts the note frequency to block and fnum values for the FM chip */
+  /* First try to compute the block -value (octave) where the note belongs */
+
+  f = freq;
+
+  octave = 5;
+
+  if (f == 0)
+    octave = 0;
+  else if (f < 261)
+    {
+      while (f < 261)
+	{
+	  octave--;
+	  f <<= 1;
+	}
+    }
+  else if (f > 493)
+    {
+      while (f > 493)
+	{
+	  octave++;
+	  f >>= 1;
+	}
+    }
+
+  if (octave > 7)
+    octave = 7;
+
+  *fnum = freq * (1 << (20 - octave)) / 49716;
+  *block = octave;
+}
+
+static void
+opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
+{
+  int             i;
+
+  /*
+   * The original 2-OP synth requires a quite long delay after writing to a
+   * register. The OPL-3 survives with just two INBs
+   */
+
+  OUTB (addr, io_addr);		/* Select register	 */
+
+  if (!opl3_enabled)
+    tenmicrosec ();
+  else
+    for (i = 0; i < 2; i++)
+      INB (io_addr);
+
+  OUTB (val, io_addr + 1);	/* Write to register	 */
+
+  if (!opl3_enabled)
+    {
+      tenmicrosec ();
+      tenmicrosec ();
+      tenmicrosec ();
+    }
+  else
+    for (i = 0; i < 2; i++)
+      INB (io_addr);
+}
+
+static void
+opl3_reset (int dev)
+{
+  int             i;
+
+  for (i = 0; i < nr_voices; i++)
+    {
+      opl3_command (physical_voices[logical_voices[i]].ioaddr,
+		KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff);	/* OP1 volume to min */
+
+      opl3_command (physical_voices[logical_voices[i]].ioaddr,
+		KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff);	/* OP2 volume to min */
+
+      if (physical_voices[logical_voices[i]].voice_mode == 4)	/* 4 OP voice */
+	{
+	  opl3_command (physical_voices[logical_voices[i]].ioaddr,
+		KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff);	/* OP3 volume to min */
+
+	  opl3_command (physical_voices[logical_voices[i]].ioaddr,
+		KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff);	/* OP4 volume to min */
+	}
+
+      opl3_kill_note (dev, i, 64);
+    }
+
+  if (opl3_enabled)
+    {
+      nr_voices = 18;
+
+      for (i = 0; i < 18; i++)
+	logical_voices[i] = i;
+
+      for (i = 0; i < 18; i++)
+	physical_voices[i].voice_mode = 2;
+
+    }
+
+}
+
+static int
+opl3_open (int dev, int mode)
+{
+  if (!opl3_ok)
+    return RET_ERROR (ENXIO);
+  if (opl3_busy)
+    return RET_ERROR (EBUSY);
+  opl3_busy = 1;
+
+  connection_mask = 0x00;	/* Just 2 OP voices */
+  if (opl3_enabled)
+    opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+  return 0;
+}
+
+static void
+opl3_close (int dev)
+{
+  opl3_busy = 0;
+  nr_voices = opl3_enabled ? 18 : 9;
+  fm_info.nr_drums = 0;
+  fm_info.perc_mode = 0;
+
+  opl3_reset (dev);
+}
+
+static void
+opl3_hw_control (int dev, unsigned char *event)
+{
+}
+
+static int
+opl3_load_patch (int dev, int format, snd_rw_buf * addr,
+		 int offs, int count, int pmgr_flag)
+{
+  struct sbi_instrument ins;
+
+  if (count < sizeof (ins))
+    {
+      printk ("FM Error: Patch record too short\n");
+      return RET_ERROR (EINVAL);
+    }
+
+  COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs);
+
+  if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+    {
+      printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+      return RET_ERROR (EINVAL);
+    }
+  ins.key = format;
+
+  return store_instr (ins.channel, &ins);
+}
+
+static void
+opl3_panning (int dev, int voice, int pressure)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+      tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+      if (pressure > 110) \
+	tmp |= 0x40;		/* Vibrato on */ \
+      opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void
+opl3_aftertouch (int dev, int voice, int pressure)
+{
+  int             tmp;
+  struct sbi_instrument *instr;
+  struct physical_voice_info *map;
+
+  if (voice < 0 || voice >= nr_voices)
+    return;
+
+  map = &physical_voices[logical_voices[voice]];
+
+  DEB (printk ("Aftertouch %d\n", voice));
+
+  if (map->voice_mode == 0)
+    return;
+
+  /*
+   * Adjust the amount of vibrato depending the pressure
+   */
+
+  instr = active_instrument[voice];
+
+  if (!instr)
+    instr = &instrmap[0];
+
+  if (voices[voice].mode == 4)
+    {
+      int             connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+      switch (connection)
+	{
+	case 0:
+	  SET_VIBRATO (4);
+	  break;
+
+	case 1:
+	  SET_VIBRATO (2);
+	  SET_VIBRATO (4);
+	  break;
+
+	case 2:
+	  SET_VIBRATO (1);
+	  SET_VIBRATO (4);
+	  break;
+
+	case 3:
+	  SET_VIBRATO (1);
+	  SET_VIBRATO (3);
+	  SET_VIBRATO (4);
+	  break;
+
+	}
+      /* Not implemented yet */
+    }
+  else
+    {
+      SET_VIBRATO (1);
+
+      if ((instr->operators[10] & 0x01))	/* Additive synthesis */
+	SET_VIBRATO (2);
+    }
+}
+
+#undef SET_VIBRATO
+
+static void
+opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+  unsigned char   data;
+  int             block, fnum, freq;
+  struct physical_voice_info *map;
+
+  if (voice < 0 || voice >= nr_voices)
+    return;
+
+  map = &physical_voices[logical_voices[voice]];
+
+  if (map->voice_mode == 0)
+    return;
+
+  switch (ctrl_num)
+    {
+    case CTRL_PITCH_BENDER:
+      voices[voice].bender = value;
+      if (!value)
+	return;
+      if (!(voices[voice].keyon_byte & 0x20))
+	return;			/* Not keyed on */
+
+      freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+      voices[voice].current_freq = freq;
+
+      freq_to_fnum (freq, &block, &fnum);
+
+      data = fnum & 0xff;	/* Least significant bits of fnumber */
+      opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+      data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);	/* KEYON|OCTAVE|MS bits
+								 * of f-num */
+      voices[voice].keyon_byte = data;
+      opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+      break;
+
+    case CTRL_PITCH_BENDER_RANGE:
+      voices[voice].bender_range = value;
+      break;
+    }
+}
+
+static int
+opl3_patchmgr (int dev, struct patmgr_info *rec)
+{
+  return RET_ERROR (EINVAL);
+}
+
+static struct synth_operations opl3_operations =
+{
+  &fm_info,
+  SYNTH_TYPE_FM,
+  FM_TYPE_ADLIB,
+  opl3_open,
+  opl3_close,
+  opl3_ioctl,
+  opl3_kill_note,
+  opl3_start_note,
+  opl3_set_instr,
+  opl3_reset,
+  opl3_hw_control,
+  opl3_load_patch,
+  opl3_aftertouch,
+  opl3_controller,
+  opl3_panning,
+  opl3_patchmgr
+};
+
+long
+opl3_init (long mem_start)
+{
+  int             i;
+
+  synth_devs[num_synths++] = &opl3_operations;
+  fm_model = 0;
+  opl3_ok = 1;
+  if (opl3_enabled)
+    {
+      printk (" <Yamaha OPL-3 FM>");
+      fm_model = 2;
+      nr_voices = 18;
+      fm_info.nr_drums = 0;
+      fm_info.capabilities |= SYNTH_CAP_OPL3;
+#ifndef SCO
+      strcpy (fm_info.name, "Yamaha OPL-3");
+#endif
+
+      for (i = 0; i < 18; i++)
+	if (physical_voices[i].ioaddr == USE_LEFT)
+	  physical_voices[i].ioaddr = left_address;
+	else
+	  physical_voices[i].ioaddr = right_address;
+
+
+      opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE);	/* Enable OPL-3 mode */
+      opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00);	/* Select all 2-OP
+									 * voices */
+    }
+  else
+    {
+      printk (" <Yamaha 2-OP FM>");
+      fm_model = 1;
+      nr_voices = 9;
+      fm_info.nr_drums = 0;
+
+      for (i = 0; i < 18; i++)
+	physical_voices[i].ioaddr = left_address;
+    };
+
+  already_initialized = 1;
+  for (i = 0; i < SBFM_MAXINSTR; i++)
+    instrmap[i].channel = -1;
+
+  return mem_start;
+}
+
+#endif
diff --git a/drivers/sound/opl3.h b/drivers/sound/opl3.h
new file mode 100644
index 0000000..ea7901f
--- /dev/null
+++ b/drivers/sound/opl3.h
@@ -0,0 +1,260 @@
+/*
+ *	opl3.h	- Definitions of the OPL-3 registers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ *	The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ *	of the right side.
+ *
+ *	Another special register at the right side is at offset 4. It contains
+ *	a bit mask defining which voices are used as 4 OP voices.
+ *
+ *	The percussive mode is implemented in the left side only.
+ *
+ *	With the above exeptions the both sides can be operated independently.
+ *	
+ *	A 4 OP voice can be created by setting the corresponding
+ *	bit at offset 4 of the right side.
+ *
+ *	For example setting the rightmost bit (0x01) changes the
+ *	first voice on the right side to the 4 OP mode. The fourth
+ *	voice is made inaccessible.
+ *
+ *	If a voice is set to the 2 OP mode, it works like 2 OP modes
+ *	of the original YM3812 (AdLib). In addition the voice can 
+ *	be connected the left, right or both stereo channels. It can
+ *	even be left unconnected. This works with 4 OP voices also.
+ *
+ *	The stereo connection bits are located in the FEEDBACK_CONNECTION
+ *	register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ *	in the second half of the voice.
+ */
+
+/*
+ *	Register numbers for the global registers
+ */
+
+#define TEST_REGISTER				0x01
+#define   ENABLE_WAVE_SELECT		0x20
+
+#define TIMER1_REGISTER				0x02
+#define TIMER2_REGISTER				0x03
+#define TIMER_CONTROL_REGISTER			0x04	/* Left side */
+#define   IRQ_RESET			0x80
+#define   TIMER1_MASK			0x40
+#define   TIMER2_MASK			0x20
+#define   TIMER1_START			0x01
+#define   TIMER2_START			0x02
+
+#define CONNECTION_SELECT_REGISTER		0x04	/* Right side */
+#define   RIGHT_4OP_0			0x01
+#define   RIGHT_4OP_1			0x02
+#define   RIGHT_4OP_2			0x04
+#define   LEFT_4OP_0			0x08
+#define   LEFT_4OP_1			0x10
+#define   LEFT_4OP_2			0x20
+
+#define OPL3_MODE_REGISTER			0x05	/* Right side */
+#define   OPL3_ENABLE			0x01
+
+#define KBD_SPLIT_REGISTER			0x08	/* Left side */
+#define   COMPOSITE_SINE_WAVE_MODE	0x80		/* Don't use with OPL-3? */
+#define   KEYBOARD_SPLIT		0x40
+
+#define PERCUSSION_REGISTER			0xbd	/* Left side only */
+#define   TREMOLO_DEPTH			0x80
+#define   VIBRATO_DEPTH			0x40
+#define	  PERCUSSION_ENABLE		0x20
+#define   BASSDRUM_ON			0x10
+#define   SNAREDRUM_ON			0x08
+#define   TOMTOM_ON			0x04
+#define   CYMBAL_ON			0x02
+#define   HIHAT_ON			0x01
+
+/*
+ *	Offsets to the register banks for operators. To get the
+ *	register number just add the operator offset to the bank offset
+ *
+ *	AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+ #define AM_VIB					0x20
+ #define   TREMOLO_ON			0x80
+ #define   VIBRATO_ON			0x40
+ #define   SUSTAIN_ON			0x20
+ #define   KSR				0x10 	/* Key scaling rate */
+ #define   MULTIPLE_MASK		0x0f	/* Frequency multiplier */
+
+ /*
+  *	KSL/Total level (0x40 to 0x55)
+  */
+#define KSL_LEVEL				0x40
+#define   KSL_MASK			0xc0	/* Envelope scaling bits */
+#define   TOTAL_LEVEL_MASK		0x3f	/* Strength (volume) of OP */
+
+/*
+ *	Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY				0x60
+#define   ATTACK_MASK			0xf0
+#define   DECAY_MASK			0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE				0x80
+#define   SUSTAIN_MASK			0xf0
+#define   RELEASE_MASK			0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT			0xe0
+
+/*
+ *	Offsets to the register banks for voices. Just add to the
+ *	voice number to get the register number.
+ *
+ *	F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW				0xa0
+
+/*
+ *	F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK					0xb0
+#define	  KEYON_BIT				0x20
+#define	  BLOCKNUM_MASK				0x1c
+#define   FNUM_HIGH_MASK			0x03
+
+/*
+ *	Feedback / Connection (0xc0 to 0xc8)
+ *
+ *	These registers have two new bits when the OPL-3 mode
+ *	is selected. These bits controls connecting the voice
+ *	to the stereo channels. For 4 OP voices this bit is
+ *	defined in the second half of the voice (add 3 to the
+ *	register offset).
+ *
+ *	For 4 OP voices the connection bit is used in the
+ *	both halfs (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION				0xc0
+#define   FEEDBACK_MASK				0x0e	/* Valid just for 1st OP of a voice */
+#define   CONNECTION_BIT			0x01
+/*
+ *	In the 4 OP mode there is four possible configurations how the
+ *	operators can be connected together (in 2 OP modes there is just
+ *	AM or FM). The 4 OP connection mode is defined by the rightmost
+ *	bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
+ *
+ *	First half	Second half	Mode
+ *
+ *					 +---+
+ *					 v   |
+ *	0		0		>+-1-+--2--3--4-->
+ *
+ *
+ *					
+ *					 +---+
+ *					 |   |
+ *	0		1		>+-1-+--2-+
+ *						  |->
+ *					>--3----4-+
+ *					
+ *					 +---+
+ *					 |   |
+ *	1		0		>+-1-+-----+
+ *						   |->
+ *					>--2--3--4-+
+ *
+ *					 +---+
+ *					 |   |
+ *	1		1		>+-1-+--+
+ *						|
+ *					>--2--3-+->
+ *						|
+ *					>--4----+
+ */
+#define   STEREO_BITS				0x30	/* OPL-3 only */
+#define     VOICE_TO_LEFT		0x10
+#define     VOICE_TO_RIGHT		0x20
+
+/*
+ * 	Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+		unsigned char voice_num;
+		unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+		unsigned short ioaddr; /* I/O port (left or right side) */
+		unsigned char op[4]; /* Operator offsets */
+	};
+
+/*
+ *	There is 18 possible 2 OP voices
+ *	(9 in the left and 9 in the right).
+ *	The first OP is the modulator and 2nd is the carrier.
+ *
+ *	The first three voices in the both sides may be connected
+ *	with another voice to a 4 OP voice. For example voice 0
+ *	can be connected with voice 3. The operators of voice 3 are
+ *	used as operators 3 and 4 of the new 4 OP voice.
+ *	In this case the 2 OP voice number 0 is the 'first half' and
+ *	voice 3 is the second.
+ */
+
+#define USE_LEFT	0
+#define USE_RIGHT	1
+
+static struct physical_voice_info physical_voices[18] =
+{
+/*       No Mode Side		OP1	OP2	OP3   OP4	*/
+/*	---------------------------------------------------	*/
+	{ 0,  2, USE_LEFT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_LEFT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_LEFT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_LEFT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_LEFT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_LEFT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_LEFT,	{0x10,	0x13,	0x00, 0x00}}, /* Used by percussive voices */
+	{ 7,  2, USE_LEFT,	{0x11,	0x14,	0x00, 0x00}}, /* if the percussive mode */
+	{ 8,  2, USE_LEFT,	{0x12,	0x15,	0x00, 0x00}}, /* is selected */
+
+	{ 0,  2, USE_RIGHT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_RIGHT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_RIGHT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_RIGHT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_RIGHT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_RIGHT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_RIGHT,	{0x10,	0x13,	0x00, 0x00}},
+	{ 7,  2, USE_RIGHT,	{0x11,	0x14,	0x00, 0x00}},
+	{ 8,  2, USE_RIGHT,	{0x12,	0x15,	0x00, 0x00}}
+};
diff --git a/drivers/sound/os.h b/drivers/sound/os.h
new file mode 100644
index 0000000..4dcfa06
--- /dev/null
+++ b/drivers/sound/os.h
@@ -0,0 +1,132 @@
+/*
+ *	OS Specific settings for Linux
+ * 
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define ALLOW_SELECT
+
+#include <linux/param.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/ctype.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <sys/kd.h>
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include "soundcard.h"
+
+typedef char snd_rw_buf;
+
+#define FALSE	0
+#define TRUE	1
+
+#define COPY_FROM_USER(d, s, o, c)	memcpy_fromfs((d), &((s)[o]), (c))
+#define COPY_TO_USER(d, o, s, c)	memcpy_tofs(&((d)[o]), (s), (c))
+#define IOCTL_FROM_USER(d, s, o, c)	memcpy_fromfs((d), &((s)[o]), (c))
+#define IOCTL_TO_USER(d, o, s, c)	memcpy_tofs(&((d)[o]), (s), (c))
+
+#define GET_BYTE_FROM_USER(target, addr, offs)	target = get_fs_byte(&((addr)[offs]))
+#define GET_SHORT_FROM_USER(target, addr, offs)	target = get_fs_word(&((addr)[offs]))
+#define GET_WORD_FROM_USER(target, addr, offs)	target = get_fs_long((long*)&((addr)[offs]))
+#define PUT_WORD_TO_USER(addr, offs, data)	put_fs_long(data, (long*)&((addr)[offs]))
+#define IOCTL_IN(arg)			get_fs_long((long *)(arg))
+#define IOCTL_OUT(arg, ret)		snd_ioctl_return((int *)arg, ret)
+
+/*
+#define DEFINE_WAIT_QUEUE(name, flag) static struct wait_queue *name = NULL; static int flag = 0
+#define DEFINE_WAIT_QUEUES(name, flag) static struct wait_queue *name = {NULL}; static int flag = {0}
+#define PROCESS_ABORTING(wqueue, flags)	(current->signal & ~current->blocked)
+#define REQUEST_TIMEOUT(nticks, wqueue)	current->timeout = jiffies + (nticks);
+#define INTERRUPTIBLE_SLEEP_ON(q, f)	\
+	{f = 1;interruptible_sleep_on(&q);f=0;}
+*/
+
+struct snd_wait {
+	  int mode; int aborting;
+	};
+
+#define DEFINE_WAIT_QUEUE(name, flag) static struct wait_queue *name = NULL; \
+	static volatile struct snd_wait flag = {0}
+#define DEFINE_WAIT_QUEUES(name, flag) static struct wait_queue *name = {NULL}; \
+	static volatile struct snd_wait flag = {{0}}
+#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
+#define PROCESS_ABORTING(q, f) (f.aborting | (current->signal & ~current->blocked))
+#define SET_ABORT_FLAG(q, f) f.aborting = 1
+#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
+#define DO_SLEEP(q, f, time_limit)	\
+	{ unsigned long tl;\
+	  if (time_limit) tl = current->timeout = jiffies + (time_limit); \
+	     else tl = 0; \
+	  f.mode = WK_SLEEP;interruptible_sleep_on(&q); \
+	  if (!(f.mode & WK_WAKEUP)) \
+	   { \
+	     if (current->signal & ~current->blocked) \
+	        f.aborting = 1; \
+	     else \
+	        if (jiffies >= tl) f.mode |= WK_TIMEOUT; \
+	   } \
+	  f.mode &= ~WK_SLEEP; \
+	}
+#define SOMEONE_WAITING(f) (f.mode & WK_SLEEP)
+#define WAKE_UP(q, f)			{f.mode = WK_WAKEUP;wake_up(&q);}
+
+#define ALLOC_DMA_CHN(chn)		request_dma(chn)
+#define RELEASE_DMA_CHN(chn)		free_dma(chn)
+
+#define GET_TIME()			jiffies
+#define RELEASE_IRQ			free_irq
+#define RET_ERROR(err)			-err
+
+/* DISABLE_INTR is used to disable interrupts.
+   These macros store the current flags to the (unsigned long) variable given
+   as a parameter. RESTORE_INTR returns the interrupt ebable bit to state
+   before DISABLE_INTR or ENABLE_INTR */
+
+#define DISABLE_INTR(flags)	__asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+#define RESTORE_INTR(flags)	__asm__ __volatile__("pushl %0 ; popfl": \
+							:"r" (flags));
+/* 
+   KERNEL_MALLOC() allocates requested number of memory  and 
+   KERNEL_FREE is used to free it. 
+   These macros are never called from interrupt, in addition the
+   nbytes will never be more than 4096 bytes. Generally the driver
+   will allocate memory in blocks of 4k. If the kernel has just a
+   page level memory allocation, 4K can be safely used as the size
+   (the nbytes parameter can be ignored).
+*/
+#define KERNEL_MALLOC(nbytes)	kmalloc(nbytes, GFP_KERNEL)
+#define KERNEL_FREE(addr)	kfree(addr)
+
+#define INB	inb
+#define OUTB	outb
diff --git a/drivers/sound/pas.h b/drivers/sound/pas.h
new file mode 100644
index 0000000..4dadea3
--- /dev/null
+++ b/drivers/sound/pas.h
@@ -0,0 +1,249 @@
+/* 																*/
+/*	Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards.			*/
+/* 																*/
+/* 	Feel free to use this header file in any application you create that has support for the Media Vision			*/
+/*	Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission.			*/
+/* 																*/
+/*										- cmetz@thor.tjhsst.edu				*/
+/* 																*/
+/*	Notes:															*/
+/* 																*/
+/*	*	All of these ports go into the MVD101 multimedia controller chip, which	then signals the other chips to do	*/
+/* 		the actual work. Many ports like the FM ones functionally attach directly to the destination chip though 	*/
+/* 		they don't actually have a direct connection.									*/
+/* 																*/
+/* 	*	The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original	*/
+/* 		PAS cards are pretty defunct now, so no attempt is made here to support them. 					*/
+/*																*/
+/*	*	The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the 	*/
+/*		incompatibilities, there still are differences that need to be accounted for.					*/
+/*																*/
+/*		Card		CD-ROM interface	PCM chip		Mixer chip		FM chip			*/
+/* 		PAS Plus	Sony proprietary	(Crystal?) 8-bit DAC	National 		OPL3			*/
+/* 		PAS 16		Zilog SCSI		MVA416 16-bit Codec	MVA508 			OPL3			*/
+/* 		CDPC		Sony proprietary	Sony 16-bit Codec	National		OPL3			*/
+/*		Fusion CD 16	Sony proprietary	MVA416 16-bit Codec	MVA508			OPL3			*/
+/*		Fusion CD	Sony proprietary	(Crystal?) 8-bit DAC	National		OPL3			*/
+/* 																*/
+#define PAS_DEFAULT_BASE		0x388
+
+/*      Symbolic Name			Value 		   R W  Subsystem	Description					*/
+#define SPEAKER_CONTROL			0x61		/*   W	PC speaker 	Control register 				*/
+#define SPEAKER_CONTROL_GHOST		0x738B		/* R W	PC speaker 	Control ghost register				*/
+#define SPEAKER_TIMER_CONTROL		0x43		/*   W  PC speaker 	Timer control register				*/
+#define SPEAKER_TIMER_CONTROL_GHOST	0x778B		/* R W  PC speaker 	Timer control register ghost			*/
+#define SPEAKER_TIMER_DATA		0x42		/*   W  PC speaker 	Timer data register				*/
+#define SPEAKER_TIMER_DATA_GHOST	0x138A		/* R W  PC speaker	Timer data register ghost			*/
+
+#define WARM_BOOT			0x41		/*   W  Control		Used to detect system warm boot	  		*/
+#define WARM_BOOT_GHOST			0x7789		/* ? W  Control		Use to get the card to fake warm boot		*/
+#define MASTER_DECODE			0x9A01		/*   W  Control		Address >> 2 of card base address		*/
+#define PRESCALE_DIVIDER		0xBF8A		/* R W	PCM		Ration between Codec clock and master clock	*/
+#define WAIT_STATE			0xBF88		/* R W	Control		Four-bit bus wait-state count (~140ns ea.)	*/
+#define BOARD_REV_ID			0x2789		/* R	Control		Extended Board Revision ID			*/
+
+#define SYSTEM_CONFIGURATION_1		0x8388		/* R W	Control								*/
+	#define S_C_1_PCS_ENABLE	0x01		/* R W  PC speaker	1=enable, 0=disable PC speaker emulation	*/
+	#define S_C_1_PCM_CLOCK_SELECT	0x02		/* R W  PCM		1=14.31818Mhz/12, 0=28.224Mhz master clock	*/ 
+	#define S_C_1_FM_EMULATE_CLOCK	0x04		/* R W  FM		1=use 28.224Mhz/2, 0=use 14.31818Mhz clock	*/
+	#define S_C_1_PCS_STEREO	0x10 		/* R W  PC speaker	1=enable PC speaker stereo effect, 0=disable	*/
+	#define S_C_1_PCS_REALSOUND	0x20		/* R W  PC speaker	1=enable RealSound enhancement, 0=disable	*/
+	#define S_C_1_FORCE_EXT_RESET	0x40		/* R W  Control		Force external reset				*/
+	#define S_C_1_FORCE_INT_RESET	0x80		/* R W  Control		Force internal reset				*/
+#define SYSTEM_CONFIGURATION_2		0x8389		/* R W  Control								*/
+	#define S_C_2_PCM_OVERSAMPLING	0x03		/* R W  PCM		00=0x, 01=2x, 10=4x, 11=reserved		*/
+	#define S_C_2_PCM_16_BIT	0x04		/* R W	PCM		1=16-bit, 0=8-bit samples			*/
+#define SYSTEM_CONFIGURATION_3		0x838A		/* R W	Control								*/
+	#define S_C_3_PCM_CLOCK_SELECT	0x02		/* R W	PCM		1=use 1.008Mhz clock for PCM, 0=don't		*/
+#define SYSTEM_CONFIGURATION_4		0x838B		/* R W  Control		CD-ROM interface controls			*/
+
+#define IO_CONFIGURATION_1		0xF388		/* R W	Control								*/
+	#define I_C_1_BOOT_RESET_ENABLE	0x80		/* R W  Control		1=reset board on warm boot, 0=don't		*/
+#define IO_CONFIGURATION_2		0xF389		/* R W  Control								*/
+	#define	I_C_2_PCM_DMA_DISABLED	0x00		/* R W  PCM		PCM DMA disabled				*/
+#define IO_CONFIGURATION_3		0xF38A		/* R W	Control								*/
+	#define I_C_3_PCM_IRQ_DISABLED	0x00		/* R W  PCM		PCM IRQ disabled				*/
+
+#define COMPATIBILITY_ENABLE		0xF788		/* R W  Control								*/
+	#define C_E_MPU401_ENABLE	0x01		/* R W	MIDI		1=enable, 0=disable MPU401 MIDI emulation	*/
+	#define C_E_SB_ENABLE		0x02		/* R W  PCM		1=enable, 0=disable Sound Blaster emulation	*/
+	#define C_E_SB_ACTIVE		0x04		/* R    PCM		"Sound Blaster Interrupt active"		*/
+	#define C_E_MPU401_ACTIVE	0x08		/* R	MIDI		"MPU UART mode active"				*/
+	#define C_E_PCM_COMPRESSION	0x10		/* R W  PCM		1=enable, 0=disabled compression		*/
+#define EMULATION_ADDRESS		0xF789		/* R W  Control								*/
+	#define E_A_SB_BASE		0x0f		/* R W  PCM		bits A4-A7 for SB base port			*/
+	#define E_A_MPU401_BASE		0xf0		/* R W	MIDI		bits A4-A7 for MPU401 base port 		*/
+#define EMULATION_CONFIGURATION		0xFB8A		/* R W			***** Only valid on newer PAS2 cards (?) *****	*/
+	#define E_C_MPU401_IRQ		0x07		/* R W	MIDI		MPU401 emulation IRQ				*/
+	#define E_C_SB_IRQ		0x38		/* R W  PCM		SB emulation IRQ				*/
+	#define E_C_SB_DMA		0xC0		/* R W	PCM		SB emulation DMA				*/
+
+#define OPERATION_MODE_1		0xEF8B		/* R	Control								*/
+	#define	O_M_1_CDROM_TYPE	0x03		/* R	CD-ROM		3=SCSI, 2=Sony, 0=no CD-ROM interface		*/
+	#define O_M_1_FM_TYPE		0x04		/* R	FM		1=sterero, 0=mono FM chip			*/
+	#define O_M_1_PCM_TYPE 		0x08		/* R	PCM		1=16-bit Codec, 0=8-bit DAC			*/
+#define OPERATION_MODE_2		0xFF8B		/* R	Control								*/
+	#define O_M_2_PCS_ENABLED	0x02		/* R	PC speaker	PC speaker emulation 1=enabled, 0=disabled	*/
+	#define O_M_2_BUS_TIMING	0x10		/* R	Control		1=AT bus timing, 0=XT bus timing		*/
+	#define O_M_2_BOARD_REVISION	0xe0		/* R	Control		Board revision					*/
+
+#define INTERRUPT_MASK			0x0B8B		/* R W	Control								*/
+	#define I_M_FM_LEFT_IRQ_ENABLE	0x01		/* R W	FM		Enable FM left interrupt			*/
+	#define I_M_FM_RIGHT_IRQ_ENABLE	0x02		/* R W	FM		Enable FM right interrupt			*/
+	#define I_M_PCM_RATE_IRQ_ENABLE	0x04		/* R W	PCM		Enable Sample Rate interrupt			*/
+	#define I_M_PCM_BUFFER_IRQ_ENABLE 0x08		/* R W	PCM		Enable Sample Buffer interrupt			*/
+	#define I_M_MIDI_IRQ_ENABLE	0x10		/* R W	MIDI		Enable MIDI interrupt				*/
+	#define I_M_BOARD_REV		0xE0		/* R	Control		Board revision					*/
+
+#define INTERRUPT_STATUS		0x0B89		/* R W	Control								*/
+	#define I_S_FM_LEFT_IRQ		0x01		/* R W	FM		Left FM Interrupt Pending			*/
+	#define I_S_FM_RIGHT_IRQ	0x02		/* R W	FM		Right FM Interrupt Pending			*/
+	#define I_S_PCM_SAMPLE_RATE_IRQ	0x04		/* R W	PCM		Sample Rate Interrupt Pending			*/
+	#define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08		/* R W	PCM		Sample Buffer Interrupt Pending			*/
+	#define I_S_MIDI_IRQ		0x10		/* R W	MIDI		MIDI Interrupt Pending				*/
+	#define I_S_PCM_CHANNEL		0x20		/* R W	PCM		1=right, 0=left					*/
+	#define I_S_RESET_ACTIVE	0x40		/* R W	Control		Reset is active (Timed pulse not finished)	*/
+	#define I_S_PCM_CLIPPING	0x80		/* R W	PCM		Clipping has occurred				*/
+
+#define FILTER_FREQUENCY		0x0B8A		/* R W	Control								*/
+	#define F_F_FILTER_DISABLED	0x00		/* R W 	Mixer		No filter					*/
+#if 0
+	struct {					/* R W	Mixer		Filter translation				*/
+		unsigned int freq:24;
+		unsigned int value:8;
+	} F_F_FILTER_translate[] = 
+	{ { 73500, 0x01 },	/* 73500Hz - divide by  16 */
+	  { 65333, 0x02 },	/* 65333Hz - divide by  18 */
+	  { 49000, 0x09 },	/* 49000Hz - divide by  24 */
+	  { 36750, 0x11 },	/* 36750Hz - divide by  32 */
+	  { 24500, 0x19 },	/* 24500Hz - divide by  48 */
+	  { 18375, 0x07 },	/* 18375Hz - divide by  64 */
+	  { 12783, 0x0f }, 	/* 12783Hz - divide by  92 */
+	  { 12250, 0x04 },	/* 12250Hz - divide by  96 */
+	  {  9188, 0x17 }, 	/*  9188Hz - divide by 128 */
+	  {  6125, 0x1f },	/*  6125Hz - divide by 192 */
+	};
+#endif
+	#define F_F_MIXER_UNMUTE	0x20		/* R W	Mixer		1=disable, 0=enable board mute			*/
+	#define F_F_PCM_RATE_COUNTER	0x40		/* R W	PCM		1=enable, 0=disable sample rate counter		*/
+	#define F_F_PCM_BUFFER_COUNTER	0x80		/* R W 	PCM		1=enable, 0=disable sample buffer counter	*/
+
+#define PAS_NONE	0
+#define PAS_PLUS	1
+#define PAS_CDPC	2
+#define PAS_16		3
+
+#ifdef DEFINE_TRANSLATIONS
+	char I_C_2_PCM_DMA_translate[] = 		/* R W  PCM		PCM DMA channel value translations		*/
+			{ 4, 1, 2, 3, 0, 5, 6, 7 };
+	char I_C_3_PCM_IRQ_translate[] = 		/* R W	PCM		PCM IRQ level value translation			*/
+		{ 0,  0,  1,  2,  3,  4,  5,  6, 0,  0,  7,  8,  9,  0, 10, 11 };  
+	char E_C_MPU401_IRQ_translate[] = 		/* R W	MIDI		MPU401 emulation IRQ value translation		*/
+		{ 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 };
+	char E_C_SB_IRQ_translate[] = 			/* R W	PCM		SB emulation IRQ translate			*/
+		{ 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 };
+	char E_C_SB_DMA_translate[] = 			/* R W	PCM		SB emulation DMA translate			*/
+		{ 0x00, 0x40, 0x80, 0xC0 };
+	char O_M_1_to_card[] = 				/* R W	Control		Translate (OM1 & 0x0f) to card type		*/
+		{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 2, 3 };   
+#else
+	extern char I_C_2_PCM_DMA_translate[];		/* R W  PCM		PCM DMA channel value translations		*/
+	extern char I_C_3_PCM_IRQ_translate[];		/* R W	PCM		PCM IRQ level value translation			*/
+	extern char E_C_MPU401_IRQ_translate[];		/* R W	MIDI		MPU401 emulation IRQ value translation		*/
+	extern char E_C_SB_IRQ_translate[];		/* R W	PCM		SB emulation IRQ translate			*/
+	extern char E_C_SB_DMA_translate[];		/* R W	PCM		SB emulation DMA translate			*/
+	extern char O_M_1_to_card[];			/* R W	Control		Translate (OM1 & 0x0f) to card type		*/
+#endif
+
+#define PARALLEL_MIXER			0x078B		/*   W	Mixer		Documented for MVD101 as FM Mono Right decode?? */
+	#define P_M_MV508_ADDRESS	0x80		/*   W	Mixer		MVD508	Address/mixer select			*/
+	#define P_M_MV508_DATA		0x00
+	#define P_M_MV508_LEFT		0x20		/*   W	Mixer		MVD508	Left channel select			*/
+	#define P_M_MV508_RIGHT		0x40		/*   W	Mixer		MVD508	Right channel select			*/
+	#define P_M_MV508_BOTH		0x00		/*   W	Mixer		MVD508	Both channel select			*/
+	#define P_M_MV508_MIXER		0x10		/*   W	Mixer		MVD508	Select a mixer (rather than a volume) 	*/
+	#define P_M_MV508_VOLUME	0x00
+
+	#define P_M_MV508_INPUTMIX	0x20		/*   W  Mixer		MVD508	Select mixer A				*/
+	#define P_M_MV508_OUTPUTMIX	0x00		/*   W  Mixer		MVD508	Select mixer B				*/
+
+	#define P_M_MV508_MASTER_A	0x01		/*   W	Mixer		MVD508	Master volume control A (output)	*/
+	#define P_M_MV508_MASTER_B	0x02		/*   W	Mixer		MVD508	Master volume control B (DSP input)	*/
+	#define P_M_MV508_BASS		0x03		/*   W	Mixer		MVD508	Bass control				*/
+	#define P_M_MV508_TREBLE	0x04		/*   W	Mixer		MVD508	Treble control				*/
+	#define P_M_MV508_MODE		0x05		/*   W	Mixer		MVD508	Master mode control			*/
+
+	#define P_M_MV508_LOUDNESS	0x04		/*   W	Mixer		MVD508	Mode control - Loudness filter 		*/
+	#define P_M_MV508_ENHANCE_BITS	0x03
+	#define P_M_MV508_ENHANCE_NONE	0x00		/*   W	Mixer		MVD508	Mode control - No stereo enhancement	*/
+	#define P_M_MV508_ENHANCE_40	0x01		/*   W	Mixer		MVD508	Mode control - 40% stereo enhancement	*/
+	#define P_M_MV508_ENHANCE_60	0x02		/*   W	Mixer		MVD508	Mode control - 60% stereo enhancement	*/
+	#define P_M_MV508_ENHANCE_80	0x03		/*   W	Mixer		MVD508	Mode control - 80% stereo enhancement	*/
+
+	#define P_M_MV508_FM		0x00		/*   W	Mixer		MVD508	Channel 0 - FM				*/
+	#define P_M_MV508_IMIXER	0x01		/*   W	Mixer		MVD508	Channel 1 - Input mixer (rec monitor)	*/
+	#define P_M_MV508_LINE		0x02		/*   W	Mixer		MVD508	Channel 2 - Line in			*/
+	#define P_M_MV508_CDROM		0x03		/*   W	Mixer		MVD508	Channel 3 - CD-ROM			*/
+	#define P_M_MV508_MIC		0x04		/*   W	Mixer		MVD508	Channel 4 - Microphone			*/
+	#define P_M_MV508_PCM		0x05		/*   W	Mixer		MVD508	Channel 5 - PCM				*/
+	#define P_M_MV508_SPEAKER	0x06		/*   W	Mixer		MVD508	Channel 6 - PC Speaker			*/
+	#define P_M_MV508_SB		0x07		/*   W	Mixer		MVD508	Channel 7 - SB DSP			*/
+
+#define SERIAL_MIXER			0xB88		/* R W	Control		Serial mixer control (used other ways)		*/
+	#define S_M_PCM_RESET		0x01		/* R W	PCM		Codec/DSP reset					*/
+	#define S_M_FM_RESET		0x02		/* R W	FM		FM chip reset					*/
+	#define S_M_SB_RESET		0x04		/* R W	PCM		SB emulation chip reset				*/
+	#define S_M_MIXER_RESET		0x10		/* R W	Mixer		Mixer chip reset				*/
+	#define S_M_INTEGRATOR_ENABLE	0x40		/* R W	Speaker		Enable PC speaker integrator (FORCE RealSound)	*/
+	#define S_M_OPL3_DUAL_MONO	0x80		/* R W  FM		Set the OPL-3 to dual mono mode			*/
+
+#define PCM_CONTROL			0xF8A		/* R W	PCM		PCM Control Register				*/
+        #define P_C_MIXER_CROSS_FIELD	0x0f
+	#define P_C_MIXER_CROSS_R_TO_R	0x01		/* R W	Mixer		Connect Right to Right				*/
+	#define P_C_MIXER_CROSS_L_TO_R	0x02		/* R W	Mixer		Connect Left  to Right				*/
+	#define P_C_MIXER_CROSS_R_TO_L	0x04		/* R W	Mixer		Connect Right to Left				*/
+	#define P_C_MIXER_CROSS_L_TO_L	0x08		/* R W	Mixer		Connect Left  to Left				*/
+	#define P_C_PCM_DAC_MODE	0x10		/* R W	PCM		Playback (DAC) mode				*/
+	#define P_C_PCM_ADC_MODE	0x00		/* R W	PCM		Record (ADC) mode				*/
+	#define P_C_PCM_MONO		0x20		/* R W	PCM		Mono mode					*/
+	#define P_C_PCM_STEREO		0x00		/* R W	PCM		Stereo mode					*/
+	#define P_C_PCM_ENABLE		0x40		/* R W	PCM		Enable PCM engine				*/
+	#define P_C_PCM_DMA_ENABLE	0x80		/* R W	PCM		Enable DRQ					*/
+
+#define SAMPLE_COUNTER_CONTROL		0x138B		/* R W	PCM		Sample counter control register			*/
+	#define S_C_C_SQUARE_WAVE	0x04		/* R W	PCM		Square wave generator (use for sample rate)	*/
+	#define S_C_C_RATE		0x06		/* R W	PCM		Rate generator (use for sample buffer count)	*/
+	#define S_C_C_LSB_THEN_MSB	0x30		/* R W	PCM		Change all 16 bits, LSB first, then MSB		*/
+
+	/* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */
+	#define S_C_C_SAMPLE_RATE	0x00		/* R W	PCM		Select sample rate timer			*/
+	#define S_C_C_SAMPLE_BUFFER	0x40		/* R W	PCM		Select sample buffer counter			*/
+
+	#define S_C_C_PC_SPEAKER	0x80		/* R W	PCM		Select PC speaker counter			*/
+
+#define SAMPLE_RATE_TIMER		0x1388		/*   W	PCM		Sample rate timer register (PCM wait interval)	*/
+#define SAMPLE_BUFFER_COUNTER		0x1389		/* R W	PCM		Sample buffer counter (DMA buffer size)		*/
+
+#define MIDI_CONTROL			0x178b		/* R W  MIDI		Midi control register				*/
+	#define M_C_ENA_TSTAMP_IRQ	0x01		/* R W	MIDI		Enable Time Stamp Interrupts			*/
+	#define M_C_ENA_TME_COMP_IRQ	0x02		/* R W  MIDI		Enable time compare interrupts			*/
+	#define M_C_ENA_INPUT_IRQ	0x04		/* R W  MIDI		Enable input FIFO interrupts			*/
+	#define M_C_ENA_OUTPUT_IRQ	0x08		/* R W  MIDI		Enable output FIFO interrupts			*/
+	#define M_C_ENA_OUTPUT_HALF_IRQ	0x10		/* R W  MIDI		Enable output FIFO half full interrupts		*/
+	#define M_C_RESET_INPUT_FIFO	0x20		/* R W  MIDI		Reset input FIFO pointer			*/
+	#define M_C_RESET_OUTPUT_FIFO	0x40		/* R W  MIDI		Reset output FIFO pointer			*/
+	#define M_C_ENA_THRU_MODE	0x80		/* R W  MIDI		Echo input to output (THRU)			*/
+
+#define MIDI_STATUS			0x1B88		/* R W  MIDI		Midi (interrupt) status register		*/
+	#define M_S_TIMESTAMP		0x01		/* R W  MIDI		Midi time stamp interrupt occurred		*/
+	#define M_S_COMPARE		0x02		/* R W  MIDI		Midi compare time interrupt occurred		*/
+	#define M_S_INPUT_AVAIL		0x04		/* R W  MIDI		Midi input data available interrupt occurred	*/
+	#define M_S_OUTPUT_EMPTY	0x08		/* R W  MIDI		Midi output FIFO empty interrupt occurred	*/
+	#define M_S_OUTPUT_HALF_EMPTY	0x10		/* R W  MIDI		Midi output FIFO half empty interrupt occurred	*/
+	#define M_S_INPUT_OVERRUN	0x20		/* R W  MIDI		Midi input overrun error occurred		*/
+	#define M_S_OUTPUT_OVERRUN	0x40		/* R W  MIDI 		Midi output overrun error occurred		*/
+	#define M_S_FRAMING_ERROR	0x80		/* R W  MIDI		Midi input framing error occurred		*/
+
+#define MIDI_FIFO_STATUS		0x1B89		/* R W  MIDI		Midi fifo status				*/
+#define MIDI_DATA			0x178A		/* R W  MIDI		Midi data register				*/
+#define MIDI_INPUT_AVAILABLE		0x0f		/* RW   MIDI */ 
diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c
new file mode 100644
index 0000000..8ee4480
--- /dev/null
+++ b/drivers/sound/pas2_card.c
@@ -0,0 +1,362 @@
+#define _PAS2_CARD_C_
+#define SND_SA_INTERRUPT
+/*
+ * linux/kernel/chr_drv/sound/pas2_card.c
+ * 
+ * Detection routine for the Pro Audio Spectrum cards.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#define DEFINE_TRANSLATIONS
+#include "pas.h"
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int             translat_code;
+static int      pas_intr_mask = 0;
+static int      pas_irq = 0;
+
+static char     pas_model;
+static char    *pas_model_names[] =
+{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16"};
+
+/* pas_read() and pas_write() are equivalents of INB() and OUTB() */
+/* These routines perform the I/O address translation required */
+/* to support other than the default base address */
+
+unsigned char
+pas_read (int ioaddr)
+{
+  return INB (ioaddr ^ translat_code);
+}
+
+void
+pas_write (unsigned char data, int ioaddr)
+{
+  OUTB (data, ioaddr ^ translat_code);
+}
+
+void
+pas2_msg (char *foo)
+{
+  printk ("    PAS2: %s.\n", foo);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+void
+pasintr (int unused)
+{
+  int             status;
+
+  status = pas_read (INTERRUPT_STATUS);
+  pas_write (status, INTERRUPT_STATUS);	/* Clear interrupt */
+
+  if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
+    {
+#ifndef EXCLUDE_AUDIO
+      pas_pcm_interrupt (status, 1);
+#endif
+      status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
+    }
+  if (status & I_S_MIDI_IRQ)
+    {
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+      pas_midi_interrupt ();
+#endif
+#endif
+      status &= ~I_S_MIDI_IRQ;
+    }
+
+}
+
+static int
+set_pas_irq (int interrupt_level)
+{
+#ifdef linux
+  int             retcode;
+  struct sigaction sa;
+
+  pas_write (0xff, INTERRUPT_STATUS);	/* Reset pending interrupts */
+
+  sa.sa_handler = pasintr;
+
+#ifdef SND_SA_INTERRUPT
+  sa.sa_flags = SA_INTERRUPT;
+#else
+  sa.sa_flags = 0;
+#endif
+
+  sa.sa_mask = 0;
+  sa.sa_restorer = NULL;
+
+  retcode = irqaction (interrupt_level, &sa);
+
+  if (retcode < 0)
+    {
+      printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level);
+    }
+  return retcode;
+#else
+  /* #  error This routine does not work with this OS	 */
+#endif
+}
+
+int
+pas_set_intr (int mask)
+{
+  int             err;
+
+  if (!mask)
+    return 0;
+
+  if (!pas_intr_mask)
+    {
+      if ((err = set_pas_irq (pas_irq)) < 0)
+	return err;
+    }
+  pas_intr_mask |= mask;
+
+  pas_write (pas_intr_mask, INTERRUPT_MASK);
+  return 0;
+}
+
+int
+pas_remove_intr (int mask)
+{
+  if (!mask)
+    return 0;
+
+  pas_intr_mask &= ~mask;
+  pas_write (pas_intr_mask, INTERRUPT_MASK);
+
+  if (!pas_intr_mask)
+    {
+      RELEASE_IRQ (pas_irq);
+    }
+  return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+int
+config_pas_hw (struct address_info *hw_config)
+{
+  char            ok = 1;
+
+  pas_irq = hw_config->irq;
+
+  pas_write (0x00, INTERRUPT_MASK);
+
+  pas_write (0x36, SAMPLE_COUNTER_CONTROL);	/* Local timer control
+						 * register */
+
+  pas_write (0x36, SAMPLE_RATE_TIMER);	/* Sample rate timer (16 bit) */
+  pas_write (0, SAMPLE_RATE_TIMER);
+
+  pas_write (0x74, SAMPLE_COUNTER_CONTROL);	/* Local timer control
+						 * register */
+
+  pas_write (0x74, SAMPLE_BUFFER_COUNTER);	/* Sample count register (16
+						 * bit) */
+  pas_write (0, SAMPLE_BUFFER_COUNTER);
+
+  pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
+  pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
+  pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* | S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER);
+
+  pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1);
+
+  if (pas_irq < 0 || pas_irq > 15)
+    {
+      printk ("PAS2: Invalid IRQ %d", pas_irq);
+      ok = 0;
+    }
+  else
+    {
+      pas_write (I_C_3_PCM_IRQ_translate[pas_irq], IO_CONFIGURATION_3);
+      if (!I_C_3_PCM_IRQ_translate[pas_irq])
+	{
+	  printk ("PAS2: Invalid IRQ %d", pas_irq);
+	  ok = 0;
+	}
+    }
+
+  if (hw_config->dma < 0 || hw_config->dma > 7)
+    {
+      printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+      ok = 0;
+    }
+  else
+    {
+      pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
+      if (!I_C_2_PCM_DMA_translate[hw_config->dma])
+	{
+	  printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+	  ok = 0;
+	}
+    }
+
+#ifdef BROKEN_BUS_CLOCK
+  pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
+#else
+  /* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1);     */
+  pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
+#endif
+  /* pas_write(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);	Don't do this	 */
+  pas_write (0x18, SYSTEM_CONFIGURATION_3);	/* ??? */
+
+  pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY);	/* Sets mute off and
+								 * selects filter rate
+								 * of 17.897 kHz */
+
+  if (pas_model == PAS_16)
+    pas_write (8, PRESCALE_DIVIDER);
+  else
+    pas_write (0, PRESCALE_DIVIDER);
+
+  pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
+  pas_write (5, PARALLEL_MIXER);
+
+#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
+
+  /* Turn on Sound Blaster compatibility */
+  /* bit 1 = SB emulation */
+  /* bit 0 = MPU401 emulation (CDPC only :-( ) */
+  pas_write (0x02, COMPATIBILITY_ENABLE);
+
+  /* "Emulation address"	 */
+  pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS);
+#endif
+
+  if (!ok)
+    pas2_msg ("Driver not enabled");
+
+  return ok;
+}
+
+int
+detect_pas_hw (struct address_info *hw_config)
+{
+  unsigned char   board_id, foo;
+
+  /*
+   * WARNING: Setting an option like W:1 or so that disables warm boot reset
+   * of the card will screw up this detect code something fierce. Adding code
+   * to handle this means possibly interfering with other cards on the bus if
+   * you have something on base port 0x388. SO be forewarned.
+   */
+
+  OUTB (0xBC, MASTER_DECODE);	/* Talk to first board */
+  OUTB (hw_config->io_base >> 2, MASTER_DECODE);	/* Set base address */
+  translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
+  pas_write (1, WAIT_STATE);	/* One wait-state */
+
+  board_id = pas_read (INTERRUPT_MASK);
+
+  if (board_id == 0xff)
+    return 0;
+
+  /*
+   * We probably have a PAS-series board, now check for a PAS2-series board
+   * by trying to change the board revision bits. PAS2-series hardware won't
+   * let you do this - the bits are read-only.
+   */
+
+  foo = board_id ^ 0xe0;
+
+  pas_write (foo, INTERRUPT_MASK);
+  foo = INB (INTERRUPT_MASK);
+  pas_write (board_id, INTERRUPT_MASK);
+
+  if (board_id != foo)		/* Not a PAS2 */
+    return 0;
+
+  if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]));
+
+  return pas_model;
+}
+
+long
+attach_pas_card (long mem_start, struct address_info *hw_config)
+{
+  pas_irq = hw_config->irq;
+
+  if (detect_pas_hw (hw_config))
+    {
+
+      if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]))
+	{
+	  printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
+	}
+
+      if (config_pas_hw (hw_config))
+	{
+
+#ifndef EXCLUDE_AUDIO
+	  mem_start = pas_pcm_init (mem_start, hw_config);
+#endif
+
+# if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
+
+	  sb_dsp_disable_midi ();	/* The SB emulation don't support
+					 * midi */
+# endif
+
+#ifndef EXCLUDE_YM3812
+	  enable_opl3_mode (0x388, 0x38a, 0);
+#endif
+
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+	  mem_start = pas_midi_init (mem_start);
+#endif
+#endif
+
+	  pas_init_mixer ();
+	}
+    }
+
+  printk ("\n");
+  return mem_start;
+}
+
+int
+probe_pas (struct address_info *hw_config)
+{
+  return detect_pas_hw (hw_config);
+}
+
+#endif
diff --git a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c
new file mode 100644
index 0000000..1a60590
--- /dev/null
+++ b/drivers/sound/pas2_midi.c
@@ -0,0 +1,295 @@
+/*
+ * linux/kernel/chr_drv/sound/pas2_midi.c
+ * 
+ * The low level driver for the PAS Midi Interface.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI)
+
+static int      midi_busy = 0, input_opened = 0;
+static int      my_dev;
+static volatile int ofifo_bytes = 0;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+pas_midi_open (int dev, int mode,
+	       void            (*input) (int dev, unsigned char data),
+	       void            (*output) (int dev)
+)
+{
+  int             err;
+  unsigned long   flags;
+  unsigned char   ctrl;
+
+
+  if (midi_busy)
+    {
+      printk ("PAS2: Midi busy\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  /* Reset input and output FIFO pointers */
+  pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
+	     MIDI_CONTROL);
+
+  DISABLE_INTR (flags);
+
+  if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
+    return err;
+
+  /* Enable input available and output FIFO empty interrupts */
+
+  ctrl = 0;
+  input_opened = 0;
+  midi_input_intr = input;
+
+  if (mode == OPEN_READ || mode == OPEN_READWRITE)
+    {
+      ctrl |= M_C_ENA_INPUT_IRQ;/* Enable input */
+      input_opened = 1;
+    }
+
+  if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+    {
+      ctrl |= M_C_ENA_OUTPUT_IRQ |	/* Enable output */
+	M_C_ENA_OUTPUT_HALF_IRQ;
+    }
+
+  pas_write (ctrl,
+	     MIDI_CONTROL);
+
+  /* Acknowledge any pending interrupts */
+
+  pas_write (0xff, MIDI_STATUS);
+  ofifo_bytes = 0;
+
+  RESTORE_INTR (flags);
+
+  midi_busy = 1;
+  qlen = qhead = qtail = 0;
+  return 0;
+}
+
+static void
+pas_midi_close (int dev)
+{
+
+  /* Reset FIFO pointers, disable intrs */
+  pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);
+
+  pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
+  midi_busy = 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+  int             fifo_space, x;
+
+  fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f;
+
+  if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13))	/* Fifo full */
+    {
+      return 0;			/* Upper layer will call again */
+    }
+
+  ofifo_bytes++;
+
+  pas_write (midi_byte, MIDI_DATA);
+
+  return 1;
+}
+
+static int
+pas_midi_out (int dev, unsigned char midi_byte)
+{
+
+  unsigned long   flags;
+
+  /*
+   * Drain the local queue first
+   */
+
+  DISABLE_INTR (flags);
+
+  while (qlen && dump_to_midi (tmp_queue[qhead]))
+    {
+      qlen--;
+      qhead++;
+    }
+
+  RESTORE_INTR (flags);
+
+  /*
+   * Output the byte if the local queue is empty.
+   */
+
+  if (!qlen)
+    if (dump_to_midi (midi_byte))
+      return 1;			/* OK */
+
+  /*
+   * Put to the local queue
+   */
+
+  if (qlen >= 256)
+    return 0;			/* Local queue full */
+
+  DISABLE_INTR (flags);
+
+  tmp_queue[qtail] = midi_byte;
+  qlen++;
+  qtail++;
+
+  RESTORE_INTR (flags);
+
+  return 1;
+}
+
+static int
+pas_midi_start_read (int dev)
+{
+  return 0;
+}
+
+static int
+pas_midi_end_read (int dev)
+{
+  return 0;
+}
+
+static int
+pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+  return RET_ERROR (EINVAL);
+}
+
+static void
+pas_midi_kick (int dev)
+{
+  ofifo_bytes = 0;
+}
+
+static int
+pas_buffer_status (int dev)
+{
+  return !qlen;
+}
+
+static struct midi_operations pas_midi_operations =
+{
+  {"Pro Audio Spectrum", 0},
+  pas_midi_open,
+  pas_midi_close,
+  pas_midi_ioctl,
+  pas_midi_out,
+  pas_midi_start_read,
+  pas_midi_end_read,
+  pas_midi_kick,
+  NULL,				/* command */
+  pas_buffer_status
+};
+
+long
+pas_midi_init (long mem_start)
+{
+  my_dev = num_midis;
+  midi_devs[num_midis++] = &pas_midi_operations;
+  return mem_start;
+}
+
+void
+pas_midi_interrupt (void)
+{
+  unsigned char   stat;
+  int             i, incount;
+  unsigned long   flags;
+
+  stat = pas_read (MIDI_STATUS);
+
+  if (stat & M_S_INPUT_AVAIL)	/* Input byte available */
+    {
+      incount = pas_read (MIDI_FIFO_STATUS) & 0x0f;	/* Input FIFO count */
+      if (!incount)
+	incount = 16;
+
+      for (i = 0; i < incount; i++)
+	if (input_opened)
+	  {
+	    midi_input_intr (my_dev, pas_read (MIDI_DATA));
+	  }
+	else
+	  pas_read (MIDI_DATA);	/* Flush */
+    }
+
+  if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY))
+    {
+      if (!(stat & M_S_OUTPUT_EMPTY))
+	{
+	  ofifo_bytes = 8;
+	}
+      else
+	{
+	  ofifo_bytes = 0;
+	}
+
+      DISABLE_INTR (flags);
+
+      while (qlen && dump_to_midi (tmp_queue[qhead]))
+	{
+	  qlen--;
+	  qhead++;
+	}
+
+      RESTORE_INTR (flags);
+    }
+
+  if (stat & M_S_FRAMING_ERROR)
+    printk ("MIDI framing error\n");
+
+  if (stat & M_S_OUTPUT_OVERRUN)
+    {
+      printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+      ofifo_bytes = 100;
+    }
+
+  pas_write (stat, MIDI_STATUS);/* Acknowledge interrupts */
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c
new file mode 100644
index 0000000..2ee38c9
--- /dev/null
+++ b/drivers/sound/pas2_mixer.c
@@ -0,0 +1,500 @@
+#define _PAS2_MIXER_C_
+
+/*
+ * linux/kernel/chr_drv/sound/pas2_mixer.c
+ * 
+ * Mixer routines for the Pro Audio Spectrum cards.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#include "pas.h"
+
+#define TRACE(what)		/* (what) */
+
+extern int      translat_code;
+
+static int      rec_devices = (SOUND_MASK_MIC);	/* Default recording source */
+static int      mode_control = 0;
+
+#define POSSIBLE_RECORDING_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
+					 SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+  0x3232,			/* Master Volume */
+  0x3232,			/* Bass */
+  0x3232,			/* Treble */
+  0x5050,			/* FM */
+  0x4b4b,			/* PCM */
+  0x3232,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x4b4b,			/* Mic */
+  0x4b4b,			/* CD */
+  0x6464,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x6464};			/* Recording level */
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int bits,
+	      int mixer /* Input or output mixer */ )
+{
+  int             left = left_vol * div / 100;
+  int             right = right_vol * div / 100;
+
+  /*
+   * The Revision D cards have a problem with their MVA508 interface. The
+   * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+   * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+   * 1. We don't need to do this because the call to pas_write more than
+   * compensates for the timing problems.
+   */
+
+  if (bits & P_M_MV508_MIXER)
+    {				/* Select input or output mixer */
+      left |= mixer;
+      right |= mixer;
+    }
+
+  if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
+    {				/* Bass and trebble are mono devices	 */
+      pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
+      pas_write (left, PARALLEL_MIXER);
+      right_vol = left_vol;
+    }
+  else
+    {
+      pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
+      pas_write (left, PARALLEL_MIXER);
+      pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
+      pas_write (right, PARALLEL_MIXER);
+    }
+
+  return (left_vol | (right_vol << 8));
+}
+
+void
+set_mode (int new_mode)
+{
+  pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
+  pas_write (new_mode, PARALLEL_MIXER);
+
+  mode_control = new_mode;
+}
+
+static int
+pas_mixer_set (int whichDev, unsigned int level)
+{
+  int             left, right, devmask, changed, i, mixer = 0;
+
+  TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+  left = level & 0x7f;
+  right = (level & 0x7f00) >> 8;
+
+  if (whichDev < SOUND_MIXER_NRDEVICES)
+    if ((1 << whichDev) & rec_devices)
+      mixer = P_M_MV508_INPUTMIX;
+    else
+      mixer = P_M_MV508_OUTPUTMIX;
+
+  switch (whichDev)
+    {
+    case SOUND_MIXER_VOLUME:	/* Master volume (0-63) */
+      levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
+      break;
+
+      /*
+       * Note! Bass and Treble are mono devices. Will use just the left
+       * channel.
+       */
+    case SOUND_MIXER_BASS:	/* Bass (0-12) */
+      levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
+      break;
+    case SOUND_MIXER_TREBLE:	/* Treble (0-12) */
+      levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
+      break;
+
+    case SOUND_MIXER_SYNTH:	/* Internal synthesizer (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
+      break;
+    case SOUND_MIXER_PCM:	/* PAS PCM (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
+      break;
+    case SOUND_MIXER_ALTPCM:	/* SB PCM (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
+      break;
+    case SOUND_MIXER_SPEAKER:	/* PC speaker (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
+      break;
+    case SOUND_MIXER_LINE:	/* External line (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
+      break;
+    case SOUND_MIXER_CD:	/* CD (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
+      break;
+    case SOUND_MIXER_MIC:	/* External microphone (0-31) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
+      break;
+    case SOUND_MIXER_IMIX:	/* Recording monitor (0-31) (Only available
+				 * on the Output Mixer) */
+      levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
+				       P_M_MV508_OUTPUTMIX);
+      break;
+    case SOUND_MIXER_RECLEV:	/* Recording level (0-15) */
+      levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
+      break;
+
+    case SOUND_MIXER_MUTE:
+      return 0;
+      break;
+
+    case SOUND_MIXER_ENHANCE:
+      i = 0;
+      level &= 0x7f;
+      if (level)
+	i = (level / 20) - 1;
+
+      mode_control &= ~P_M_MV508_ENHANCE_BITS;
+      mode_control |= P_M_MV508_ENHANCE_BITS;
+      set_mode (mode_control);
+
+      if (i)
+	i = (i + 1) * 20;
+      return i;
+      break;
+
+    case SOUND_MIXER_LOUD:
+      mode_control &= ~P_M_MV508_LOUDNESS;
+      if (level)
+	mode_control |= P_M_MV508_LOUDNESS;
+      set_mode (mode_control);
+      return !!level;		/* 0 or 1 */
+      break;
+
+    case SOUND_MIXER_RECSRC:
+      devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+      changed = devmask ^ rec_devices;
+      rec_devices = devmask;
+
+      for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+	if (changed & (1 << i))
+	  {
+	    pas_mixer_set (i, levels[i]);
+	  }
+      return rec_devices;
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+  return (levels[whichDev]);
+}
+
+/*****/
+
+static int
+mixer_set_levels (struct sb_mixer_levels *user_l)
+{
+#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15))
+
+  struct sb_mixer_levels l;
+
+  IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l));
+
+  if (l.master.l & ~0xF || l.master.r & ~0xF
+      || l.line.l & ~0xF || l.line.r & ~0xF
+      || l.voc.l & ~0xF || l.voc.r & ~0xF
+      || l.fm.l & ~0xF || l.fm.r & ~0xF
+      || l.cd.l & ~0xF || l.cd.r & ~0xF
+      || l.mic & ~0x7)
+    return (RET_ERROR (EINVAL));
+
+  pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master));
+  pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line));
+  pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc));
+  pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc));
+  pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm));
+  pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd));
+  pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8));
+  return (0);
+}
+
+/*
+ * This sets aspects of the Mixer that are not volume levels. (Recording
+ * source, filter level, I/O filtering, and stereo.)
+ */
+static int
+mixer_set_params (struct sb_mixer_params *user_p)
+{
+  struct sb_mixer_params p;
+  S_BYTE          val;
+  int             src;
+  unsigned long   flags;
+
+  IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
+
+  if (p.record_source != SRC_MIC
+      && p.record_source != SRC_CD
+      && p.record_source != SRC_LINE)
+    return (RET_ERROR (EINVAL));
+
+  /*
+   * I'm not sure if this is The Right Thing.  Should stereo be entirely
+   * under control of DSP?  I like being able to toggle it while a sound is
+   * playing, so I do this... because I can.
+   */
+
+  DISABLE_INTR (flags);
+
+  val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L;
+  if (!p.dsp_stereo)
+    val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R);	/* Mono */
+  pas_write (val, PCM_CONTROL);
+
+  RESTORE_INTR (flags);
+
+  switch (p.record_source)
+    {
+    case SRC_CD:
+      src = SOUND_MASK_CD;
+      break;
+
+    case SRC_LINE:
+      src = SOUND_MASK_LINE;
+      break;
+
+    default:
+      src = SOUND_MASK_MIC;
+      break;
+    }
+
+  pas_mixer_set (SOUND_MIXER_RECSRC, src);
+
+  /*
+   * setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) |
+   * (p.filter_output ? FILT_ON : FILT_OFF)));
+   */
+  return (0);
+}
+
+static int
+getmixer (int dev, int chn)
+{
+  if (chn == P_M_MV508_RIGHT)
+    {
+      return (levels[dev] >> 8) & 0x7f;
+    }
+  else
+    {
+      return levels[dev] & 0x7f;
+    }
+}
+
+/* Read the current mixer level settings into the user's struct. */
+static int
+mixer_get_levels (struct sb_mixer_levels *user_l)
+{
+
+  struct sb_mixer_levels l;
+
+  l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100;	/* Master */
+  l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100;	/* Master */
+
+  l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100;	/* Line */
+  l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+  l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100;	/* DAC */
+  l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+  l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100;	/* FM */
+  l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+  l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100;	/* CD */
+  l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+  l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100;	/* Microphone */
+
+  IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
+  return (0);
+}
+
+/* Read the current mixer parameters into the user's struct. */
+static int
+mixer_get_params (struct sb_mixer_params *user_params)
+{
+  S_BYTE          val;
+  struct sb_mixer_params params;
+
+  switch (rec_devices)
+    {
+    case SOUND_MASK_CD:
+      params.record_source = SRC_CD;
+      break;
+
+    case SOUND_MASK_LINE:
+      params.record_source = SRC_LINE;
+      break;
+
+    case SOUND_MASK_MIC:
+      params.record_source = SRC_MIC;
+      break;
+
+    default:
+      params.record_source = SRC_MIC;
+      pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC);	/* Adjust */
+    }
+
+  params.hifreq_filter = OFF;
+  params.filter_input = OFF;
+  params.filter_output = OFF;
+
+  val = INB (PCM_CONTROL);
+  params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R));
+
+  IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
+  return (0);
+}
+
+/*****/
+
+static void
+pas_mixer_reset (void)
+{
+  int             foo;
+
+  TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+  for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+    pas_mixer_set (foo, levels[foo]);
+
+  set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
+}
+
+int
+pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+  TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+  if (((cmd >> 8) & 0xff) == 'M')
+    {
+      if (cmd & IOC_IN)
+	return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+      else
+	{			/* Read parameters */
+
+	  switch (cmd & 0xff)
+	    {
+
+	    case SOUND_MIXER_RECSRC:
+	      return IOCTL_OUT (arg, rec_devices);
+	      break;
+
+	    case SOUND_MIXER_STEREODEVS:
+	      return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+	      break;
+
+	    case SOUND_MIXER_DEVMASK:
+	      return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+	      break;
+
+	    case SOUND_MIXER_RECMASK:
+	      return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+	      break;
+
+	    case SOUND_MIXER_CAPS:
+	      return IOCTL_OUT (arg, 0);	/* No special capabilities */
+	      break;
+
+	    case SOUND_MIXER_MUTE:
+	      return IOCTL_OUT (arg, 0);	/* No mute yet */
+	      break;
+
+	    case SOUND_MIXER_ENHANCE:
+	      if (!(mode_control & P_M_MV508_ENHANCE_BITS))
+		return IOCTL_OUT (arg, 0);
+	      return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
+	      break;
+
+	    case SOUND_MIXER_LOUD:
+	      if (mode_control & P_M_MV508_LOUDNESS)
+		return IOCTL_OUT (arg, 1);
+	      return IOCTL_OUT (arg, 0);
+	      break;
+
+	    default:
+	      return IOCTL_OUT (arg, levels[cmd & 0xff]);
+	    }
+	}
+    }
+  else
+    {
+      switch (cmd)
+	{
+	case MIXER_IOCTL_SET_LEVELS:
+	  mixer_set_levels ((struct sb_mixer_levels *) arg);
+	  return mixer_get_levels ((struct sb_mixer_levels *) arg);
+	case MIXER_IOCTL_SET_PARAMS:
+	  mixer_set_params ((struct sb_mixer_params *) arg);
+	  return mixer_get_params ((struct sb_mixer_params *) arg);
+	case MIXER_IOCTL_READ_LEVELS:
+	  return mixer_get_levels ((struct sb_mixer_levels *) arg);
+	case MIXER_IOCTL_READ_PARAMS:
+	  return mixer_get_params ((struct sb_mixer_params *) arg);
+	case MIXER_IOCTL_RESET:
+	  pas_mixer_reset ();
+	  return (0);
+	default:
+	  return RET_ERROR (EINVAL);
+	}
+    }
+  return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+  pas_mixer_ioctl
+};
+
+int
+pas_init_mixer (void)
+{
+  pas_mixer_reset ();
+
+  mixer_devs[num_mixers++] = &pas_mixer_operations;
+  return 1;
+}
+
+#endif
diff --git a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c
new file mode 100644
index 0000000..878de5a
--- /dev/null
+++ b/drivers/sound/pas2_pcm.c
@@ -0,0 +1,431 @@
+#define _PAS2_PCM_C_
+/*
+ * linux/kernel/chr_drv/sound/pas2_pcm.c
+ * 
+ * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
+
+#define TRACE(WHAT)		/* (WHAT) */
+
+#define PAS_PCM_INTRBITS (0x08)
+/* Sample buffer timer interrupt enable */
+
+#define PCM_NON	0
+#define PCM_DAC	1
+#define PCM_ADC	2
+
+static unsigned long pcm_speed = 0;	/* sampling rate */
+static unsigned char pcm_channels = 1;	/* channels/sample (1 or 2) */
+static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */
+static unsigned char pcm_filter = 0;	/* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count = 0;
+static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
+static int      my_devnum = 0;
+
+int
+pcm_set_speed (int arg)
+{
+  int             foo, tmp;
+  unsigned long   flags;
+
+  if (arg > 44100)
+    arg = 44100;
+  if (arg < 5000)
+    arg = 5000;
+
+  foo = 1193180 / arg;
+  arg = 1193180 / foo;
+
+  if (pcm_channels & 2)
+    foo = foo >> 1;
+
+  pcm_speed = arg;
+
+  tmp = pas_read (FILTER_FREQUENCY);
+
+  DISABLE_INTR (flags);
+
+  pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
+  pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+  pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
+  pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
+  pas_write (tmp, FILTER_FREQUENCY);
+
+  RESTORE_INTR (flags);
+
+  return pcm_speed;
+}
+
+int
+pcm_set_channels (int arg)
+{
+
+  if ((arg != 1) && (arg != 2))
+    return pcm_channels;
+
+  if (arg != pcm_channels)
+    {
+      pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
+
+      pcm_channels = arg;
+      pcm_set_speed (pcm_speed);/* The speed must be reinitialized */
+    }
+
+  return pcm_channels;
+}
+
+int
+pcm_set_bits (int arg)
+{
+  if ((arg & pcm_bitsok) != arg)
+    return pcm_bits;
+
+  if (arg != pcm_bits)
+    {
+      pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
+
+      pcm_bits = arg;
+    }
+
+  return pcm_bits;
+}
+
+static int
+pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+  TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+  switch (cmd)
+    {
+    case SOUND_PCM_WRITE_RATE:
+      if (local)
+	return pcm_set_speed (arg);
+      return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_RATE:
+      if (local)
+	return pcm_speed;
+      return IOCTL_OUT (arg, pcm_speed);
+      break;
+
+    case SNDCTL_DSP_STEREO:
+      if (local)
+	return pcm_set_channels (arg + 1) - 1;
+      return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
+      break;
+
+    case SOUND_PCM_WRITE_CHANNELS:
+      return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_CHANNELS:
+      if (local)
+	return pcm_channels;
+      return IOCTL_OUT (arg, pcm_channels);
+      break;
+
+    case SNDCTL_DSP_SAMPLESIZE:
+      if (local)
+	return pcm_set_bits (arg);
+      return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_BITS:
+      if (local)
+	return pcm_bits;
+      return IOCTL_OUT (arg, pcm_bits);
+
+    case SOUND_PCM_WRITE_FILTER:	/* NOT YET IMPLEMENTED */
+      if (IOCTL_IN (arg) > 1)
+	return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+      break;
+
+      pcm_filter = IOCTL_IN (arg);
+    case SOUND_PCM_READ_FILTER:
+      return IOCTL_OUT (arg, pcm_filter);
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+  return RET_ERROR (EINVAL);
+}
+
+static void
+pas_pcm_reset (int dev)
+{
+  TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
+
+  pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+}
+
+static int
+pas_pcm_open (int dev, int mode)
+{
+  int             err;
+
+  TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
+
+  if (mode != OPEN_READ && mode != OPEN_WRITE)
+    {
+      printk ("PAS2: Attempt to open PCM device for simultaneous read and write");
+      return RET_ERROR (EINVAL);
+    }
+
+  if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
+    return err;
+
+  if (!DMAbuf_open_dma (dev))
+    {
+      pas_remove_intr (PAS_PCM_INTRBITS);
+      return RET_ERROR (EBUSY);
+    }
+
+  pcm_count = 0;
+
+  pcm_set_bits (8);
+  pcm_set_channels (1);
+  pcm_set_speed (DSP_DEFAULT_SPEED);
+
+  return 0;
+}
+
+static void
+pas_pcm_close (int dev)
+{
+  unsigned long   flags;
+
+  TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
+
+  DISABLE_INTR (flags);
+
+  pas_pcm_reset (dev);
+  DMAbuf_close_dma (dev);
+  pas_remove_intr (PAS_PCM_INTRBITS);
+  pcm_mode = PCM_NON;
+
+  RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
+{
+  unsigned long   flags, cnt;
+
+  TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+  cnt = count;
+  if (sound_dsp_dmachan[dev] > 3)
+    cnt >>= 1;
+  cnt--;
+
+  if (sound_dma_automode[dev] &&
+      intrflag &&
+      cnt == pcm_count)
+    return;			/* Auto mode on. No need to react */
+
+  DISABLE_INTR (flags);
+
+  pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+	     PCM_CONTROL);
+
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+  if (sound_dsp_dmachan[dev] > 3)
+    count >>= 1;
+  count--;
+
+  if (count != pcm_count)
+    {
+      pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+      pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+      pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+      pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+      pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+      pcm_count = count;
+    }
+  pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+  pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+  pcm_mode = PCM_DAC;
+
+  RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag)
+{
+  unsigned long   flags;
+  int             cnt;
+
+  TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+  cnt = count;
+  if (sound_dsp_dmachan[dev] > 3)
+    cnt >>= 1;
+  cnt--;
+
+  if (sound_dma_automode[my_devnum] &&
+      intrflag &&
+      cnt == pcm_count)
+    return;			/* Auto mode on. No need to react */
+
+  DISABLE_INTR (flags);
+
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+  if (sound_dsp_dmachan[dev] > 3)
+    count >>= 1;
+
+  count--;
+
+  if (count != pcm_count)
+    {
+      pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+      pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+      pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+      pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+      pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+      pcm_count = count;
+    }
+  pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+  pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+  pcm_mode = PCM_ADC;
+
+  RESTORE_INTR (flags);
+}
+
+static int
+pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
+{
+  return 0;
+}
+static int
+pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
+{
+  return 0;
+}
+
+static struct audio_operations pas_pcm_operations =
+{
+  "Pro Audio Spectrum",
+  pas_pcm_open,			/* */
+  pas_pcm_close,		/* */
+  pas_pcm_output_block,		/* */
+  pas_pcm_start_input,		/* */
+  pas_pcm_ioctl,		/* */
+  pas_pcm_prepare_for_input,	/* */
+  pas_pcm_prepare_for_output,	/* */
+  pas_pcm_reset,		/* */
+  pas_pcm_reset,		/* halt_xfer */
+  NULL,				/* has_output_drained */
+  NULL				/* copy_from_user */
+};
+
+long
+pas_pcm_init (long mem_start, struct address_info *hw_config)
+{
+  TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
+
+  pcm_bitsok = 8;
+  if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
+    pcm_bitsok |= 16;
+
+  pcm_set_speed (DSP_DEFAULT_SPEED);
+
+  if (num_dspdevs < MAX_DSP_DEV)
+    {
+      dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations;
+      sound_dsp_dmachan[my_devnum] = hw_config->dma;
+      if (hw_config->dma > 3)
+	{
+	  sound_buffcounts[my_devnum] = 1;
+	  sound_buffsizes[my_devnum] = 2 * 65536;
+	  sound_dma_automode[my_devnum] = 1;
+	}
+      else
+	{
+	  sound_buffcounts[my_devnum] = 1;
+	  sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
+	  sound_dma_automode[my_devnum] = 1;
+	}
+    }
+  else
+    printk ("PAS2: Too many PCM devices available\n");
+
+  return mem_start;
+}
+
+void
+pas_pcm_interrupt (unsigned char status, int cause)
+{
+  if (cause == 1)		/* PCM buffer done */
+    {
+      /*
+       * Halt the PCM first. Otherwise we don't have time to start a new
+       * block before the PCM chip proceeds to the next sample
+       */
+
+      if (!sound_dma_automode[my_devnum])
+	{
+	  pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+		     PCM_CONTROL);
+	}
+
+      switch (pcm_mode)
+	{
+
+	case PCM_DAC:
+	  DMAbuf_outputintr (my_devnum);
+	  break;
+
+	case PCM_ADC:
+	  DMAbuf_inputintr (my_devnum);
+	  break;
+
+	default:
+	  printk ("PAS: Unexpected PCM interrupt\n");
+	}
+    }
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/patmgr.c b/drivers/sound/patmgr.c
new file mode 100644
index 0000000..e0cc157
--- /dev/null
+++ b/drivers/sound/patmgr.c
@@ -0,0 +1,262 @@
+/*
+ * linux/kernel/chr_drv/sound/patmgr.c
+ * 
+ * The patch maneger interface for the /dev/sequencer
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#define PATMGR_C
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)
+
+DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
+		    server_wait_flag[MAX_SYNTH_DEV]);
+
+static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
+{NULL};
+static volatile int msg_direction[MAX_SYNTH_DEV] =
+{0};
+
+static int      pmgr_opened[MAX_SYNTH_DEV] =
+{0};
+
+#define A_TO_S	1
+#define S_TO_A 	2
+
+DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);
+
+int
+pmgr_open (int dev)
+{
+  if (dev < 0 || dev >= num_synths)
+    return RET_ERROR (ENXIO);
+
+  if (pmgr_opened[dev])
+    return RET_ERROR (EBUSY);
+  pmgr_opened[dev] = 1;
+
+  RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);
+
+  return 0;
+}
+
+void
+pmgr_release (int dev)
+{
+
+  if (mbox[dev])		/* Killed in action. Inform the client */
+    {
+
+      mbox[dev]->key = PM_ERROR;
+      mbox[dev]->parm1 = RET_ERROR (EIO);
+
+      if (SOMEONE_WAITING (appl_wait_flag))
+	WAKE_UP (appl_proc, appl_wait_flag);
+    }
+
+  pmgr_opened[dev] = 0;
+}
+
+int
+pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  unsigned long   flags;
+  int             ok = 0;
+
+  if (count != sizeof (struct patmgr_info))
+    {
+      printk ("PATMGR%d: Invalid read count\n", dev);
+      return RET_ERROR (EIO);
+    }
+
+  while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+    {
+      DISABLE_INTR (flags);
+
+      while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
+	     !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+	{
+	  DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0);
+	}
+
+      if (mbox[dev] && msg_direction[dev] == A_TO_S)
+	{
+	  COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
+	  msg_direction[dev] = 0;
+	  ok = 1;
+	}
+
+      RESTORE_INTR (flags);
+
+    }
+
+  if (!ok)
+    return RET_ERROR (EINTR);
+  return count;
+}
+
+int
+pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  unsigned long   flags;
+
+  if (count < 4)
+    {
+      printk ("PATMGR%d: Write count < 4\n", dev);
+      return RET_ERROR (EIO);
+    }
+
+  COPY_FROM_USER (mbox[dev], buf, 0, 4);
+
+  if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
+    {
+      int             tmp_dev;
+
+      tmp_dev = ((unsigned short *) mbox[dev])[2];
+      if (tmp_dev != dev)
+	return RET_ERROR (ENXIO);
+
+      return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
+					  buf, 4, count, 1);
+    }
+
+  if (count != sizeof (struct patmgr_info))
+    {
+      printk ("PATMGR%d: Invalid write count\n", dev);
+      return RET_ERROR (EIO);
+    }
+
+  /*
+   * If everything went OK, there should be a preallocated buffer in the
+   * mailbox and a client waiting.
+   */
+
+  DISABLE_INTR (flags);
+
+  if (mbox[dev] && !msg_direction[dev])
+    {
+      COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
+      msg_direction[dev] = S_TO_A;
+
+      if (SOMEONE_WAITING (appl_wait_flag))
+	{
+	  WAKE_UP (appl_proc, appl_wait_flag);
+	}
+    }
+
+  RESTORE_INTR (flags);
+
+  return count;
+}
+
+int
+pmgr_access (int dev, struct patmgr_info *rec)
+{
+  unsigned long   flags;
+  int             err = 0;
+
+  DISABLE_INTR (flags);
+
+  if (mbox[dev])
+    printk ("  PATMGR: Server %d mbox full. Why?\n", dev);
+  else
+    {
+      rec->key = PM_K_COMMAND;
+      mbox[dev] = rec;
+      msg_direction[dev] = A_TO_S;
+
+      if (SOMEONE_WAITING (server_wait_flag[dev]))
+	{
+	  WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+	}
+
+      DO_SLEEP (appl_proc, appl_wait_flag, 0);
+
+      if (msg_direction[dev] != S_TO_A)
+	{
+	  rec->key = PM_ERROR;
+	  rec->parm1 = RET_ERROR (EIO);
+	}
+      else if (rec->key == PM_ERROR)
+	{
+	  err = rec->parm1;
+	  if (err > 0)
+	    err = -err;
+	}
+
+      mbox[dev] = NULL;
+      msg_direction[dev] = 0;
+    }
+
+  RESTORE_INTR (flags);
+
+  return err;
+}
+
+int
+pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
+	     unsigned long p3, unsigned long p4)
+{
+  unsigned long   flags;
+  int             err = 0;
+
+  if (!pmgr_opened[dev])
+    return 0;
+
+  DISABLE_INTR (flags);
+
+  if (mbox[dev])
+    printk ("  PATMGR: Server %d mbox full. Why?\n", dev);
+  else
+    {
+      mbox[dev] =
+	(struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info));
+
+      mbox[dev]->key = PM_K_EVENT;
+      mbox[dev]->command = event;
+      mbox[dev]->parm1 = p1;
+      mbox[dev]->parm2 = p2;
+      mbox[dev]->parm3 = p3;
+      msg_direction[dev] = A_TO_S;
+
+      if (SOMEONE_WAITING (server_wait_flag[dev]))
+	{
+	  WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+	}
+
+      DO_SLEEP (appl_proc, appl_wait_flag, 0);
+      if (mbox[dev])
+	KERNEL_FREE (mbox[dev]);
+      mbox[dev] = NULL;
+      msg_direction[dev] = 0;
+    }
+
+  RESTORE_INTR (flags);
+
+  return err;
+}
+
+#endif
diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c
new file mode 100644
index 0000000..e2527c5
--- /dev/null
+++ b/drivers/sound/sb_card.c
@@ -0,0 +1,53 @@
+
+/*
+ * linux/kernel/chr_drv/sound/sb_card.c
+ * 
+ * Detection routine for the SoundBlaster cards.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+long
+attach_sb_card (long mem_start, struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI)
+  if (!sb_dsp_detect (hw_config))
+    return mem_start;
+  mem_start = sb_dsp_init (mem_start, hw_config);
+#endif
+
+  return mem_start;
+}
+
+int
+probe_sb (struct address_info *hw_config)
+{
+  return sb_dsp_detect (hw_config);
+}
+
+#endif
diff --git a/drivers/sound/sb_dsp.c b/drivers/sound/sb_dsp.c
new file mode 100644
index 0000000..fa297ab
--- /dev/null
+++ b/drivers/sound/sb_dsp.c
@@ -0,0 +1,1342 @@
+/*
+ * linux/kernel/chr_drv/sound/sb_dsp.c
+ * 
+ * The low level driver for the SoundBlaster DS chips.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen
+ * <shaehnic@ucsd.edu>
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+#undef SB_TEST_IRQ
+
+#define DSP_RESET	(sbc_base + 0x6)
+#define DSP_READ	(sbc_base + 0xA)
+#define DSP_WRITE	(sbc_base + 0xC)
+#define DSP_COMMAND	(sbc_base + 0xC)
+#define DSP_STATUS	(sbc_base + 0xC)
+#define DSP_DATA_AVAIL	(sbc_base + 0xE)
+#define MIXER_ADDR	(sbc_base + 0x4)
+#define MIXER_DATA	(sbc_base + 0x5)
+#define OPL3_LEFT	(sbc_base + 0x0)
+#define OPL3_RIGHT	(sbc_base + 0x2)
+#define OPL3_BOTH	(sbc_base + 0x8)
+
+static int      sbc_base = 0;
+static int      sbc_irq = 0;
+
+#define POSSIBLE_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/*
+ * Mixer registers
+ * 
+ * NOTE!	RECORD_SRC == IN_FILTER
+ */
+
+#define VOC_VOL		0x04
+#define MIC_VOL		0x0A
+#define MIC_MIX		0x0A
+#define RECORD_SRC	0x0C
+#define IN_FILTER	0x0C
+#define OUT_FILTER	0x0E
+#define MASTER_VOL	0x22
+#define FM_VOL		0x26
+#define CD_VOL		0x28
+#define LINE_VOL	0x2E
+
+#define FREQ_HI         (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW        0	/* Use Low-frequency ANFI filters */
+#define FILT_ON         0	/* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF        (1 << 5)
+
+/* Convenient byte masks */
+#define B1(x)	((x) & 0x01)
+#define B2(x)	((x) & 0x03)
+#define B3(x)	((x) & 0x07)
+#define B4(x)	((x) & 0x0f)
+#define B5(x)	((x) & 0x1f)
+#define B6(x)	((x) & 0x3f)
+#define B7(x)	((x) & 0x7f)
+#define B8(x)	((x) & 0xff)
+#define F(x)	(!!(x))		/* 0 or 1 only */
+
+#define MONO_DAC	0x00
+#define STEREO_DAC	0x02
+
+/* DSP Commands */
+
+#define DSP_CMD_SPKON		0xD1
+#define DSP_CMD_SPKOFF		0xD3
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+#define IMODE_NONE		0
+#define IMODE_OUTPUT		1
+#define IMODE_INPUT		2
+#define IMODE_INIT		3
+#define IMODE_MIDI		4
+
+#define NORMAL_MIDI	0
+#define UART_MIDI	1
+
+static int      sb_dsp_ok = 0;	/* Set to 1 after successful initialization */
+static int      midi_disabled = 0;
+static int      dsp_highspeed = 0, dsp_stereo = 0;
+static int      dsp_current_speed = DSP_DEFAULT_SPEED;
+static int      sb16 = 0;
+
+#ifndef EXCLUDE_SBPRO
+static int      rec_devices = SOUND_MASK_MIC;
+static int      hi_filter = 0, filter_in = 0, filter_out = 0;
+
+#endif
+
+static int      midi_mode = NORMAL_MIDI;
+static int      midi_busy = 0;	/* 1 if the process has output to MIDI */
+static int      dsp_busy = 0;
+
+static volatile int irq_mode = IMODE_NONE;	/* IMODE_INPUT, IMODE_OUTPUT
+						 * or IMODE_NONE */
+static volatile int irq_ok = 0;
+
+static int      dsp_model = 1;	/* 1=SB, 2=SB Pro */
+static int      duplex_midi = 0;
+static int      my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int      dsp_speed (int);
+static int      dsp_set_stereo (int mode);
+static int      dsp_command (unsigned char val);
+
+#ifndef EXCLUDE_SBPRO
+static void     setmixer (unsigned char port, unsigned char value);
+static int      getmixer (unsigned char port);
+static void     init_mixer (void);
+static int      detect_mixer (void);
+
+#endif
+
+#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
+
+/* Common code for the midi and pcm functions */
+
+static int
+dsp_command (unsigned char val)
+{
+  int             i, limit;
+
+  limit = GET_TIME () + 10;	/* The timeout is 0.1 secods */
+
+  /*
+   * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+   * called while interrupts are disabled. This means that the timer is
+   * disabled also. However the timeout situation is a abnormal condition.
+   * Normally the DSP should be ready to accept commands after just couple of
+   * loops.
+   */
+
+  for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
+    {
+      if ((INB (DSP_STATUS) & 0x80) == 0)
+	{
+	  OUTB (val, DSP_COMMAND);
+	  return 1;
+	}
+    }
+
+  printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val);
+  printk ("IRQ conflict???\n");
+  return 0;
+}
+
+void
+sbintr (int unused)
+{
+  int             status, data;
+
+#ifndef EXCLUDE_SBPRO
+  if (sb16)
+    {
+      unsigned char   src = getmixer (0x82);	/* Interrupt status register */
+
+      if (!(src & 1))
+	return;			/* Not a DSP interupt */
+    }
+#endif
+
+  status = INB (DSP_DATA_AVAIL);/* Clear interrupt */
+
+  if (intr_active)
+    switch (irq_mode)
+      {
+      case IMODE_OUTPUT:
+	intr_active = 0;
+	DMAbuf_outputintr (my_dev);
+	break;
+
+      case IMODE_INPUT:
+	intr_active = 0;
+	DMAbuf_inputintr (my_dev);
+	/* A complete buffer has been input. Let's start new one */
+	break;
+
+      case IMODE_INIT:
+	intr_active = 0;
+	irq_ok = 1;
+	break;
+
+      case IMODE_MIDI:
+	printk ("+");
+	data = INB (DSP_READ);
+	printk ("%02x", data);
+
+	break;
+
+      default:
+	printk ("SoundBlaster: Unexpected interrupt\n");
+      }
+}
+
+static int
+set_dsp_irq (int interrupt_level)
+{
+  int             retcode;
+
+#ifdef linux
+  struct sigaction sa;
+
+  sa.sa_handler = sbintr;
+
+#ifdef SND_SA_INTERRUPT
+  sa.sa_flags = SA_INTERRUPT;
+#else
+  sa.sa_flags = 0;
+#endif
+
+  sa.sa_mask = 0;
+  sa.sa_restorer = NULL;
+
+  retcode = irqaction (interrupt_level, &sa);
+
+  if (retcode < 0)
+    {
+      printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level);
+    }
+
+#else
+  /* #  error Unimplemented for this OS	 */
+#endif
+  return retcode;
+}
+
+static int
+reset_dsp (void)
+{
+  int             loopc;
+
+  OUTB (1, DSP_RESET);
+  tenmicrosec ();
+  OUTB (0, DSP_RESET);
+  tenmicrosec ();
+  tenmicrosec ();
+  tenmicrosec ();
+
+  for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++);	/* Wait for data
+										 * available status */
+
+  if (INB (DSP_READ) != 0xAA)
+    return 0;			/* Sorry */
+
+  return 1;
+}
+
+#endif
+
+#ifndef EXCLUDE_AUDIO
+
+static void
+dsp_speaker (char state)
+{
+  if (state)
+    dsp_command (DSP_CMD_SPKON);
+  else
+    dsp_command (DSP_CMD_SPKOFF);
+}
+
+static int
+dsp_speed (int speed)
+{
+  unsigned char   tconst;
+  unsigned long   flags;
+
+
+  if (speed < 4000)
+    speed = 4000;
+
+  if (speed > 44100)
+    speed = 44100;		/* Invalid speed */
+
+  if (dsp_model == 1 && speed > 22050)
+    speed = 22050;
+  /* SB Classic doesn't support higher speed */
+
+
+  if (dsp_stereo && speed > 22050)
+    speed = 22050;
+  /* Max. stereo speed is 22050 */
+
+  if ((speed > 22050) && midi_busy)
+    {
+      printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
+      speed = 22050;
+    }
+
+  if (dsp_stereo)
+    speed <<= 1;
+
+  /* Now the speed should be valid */
+
+  if (speed > 22050)
+    {				/* High speed mode */
+      tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8);
+      dsp_highspeed = 1;
+
+      DISABLE_INTR (flags);
+      if (dsp_command (0x40))
+	dsp_command (tconst);
+      RESTORE_INTR (flags);
+
+      speed = (256000000 / (65536 - (tconst << 8)));
+    }
+  else
+    {
+      dsp_highspeed = 0;
+      tconst = (256 - (1000000 / speed)) & 0xff;
+
+      DISABLE_INTR (flags);
+      if (dsp_command (0x40))	/* Set time constant */
+	dsp_command (tconst);
+      RESTORE_INTR (flags);
+
+      speed = 1000000 / (256 - tconst);
+    }
+
+  if (dsp_stereo)
+    speed >>= 1;
+
+  dsp_current_speed = speed;
+  return speed;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+  dsp_stereo = 0;
+
+  if (dsp_model == 1 || sb16)
+    return 0;			/* Sorry no stereo */
+
+  if (mode && midi_busy)
+    {
+      printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
+      return 0;
+    }
+
+  dsp_stereo = !!mode;
+
+#ifndef EXCLUDE_SBPRO
+  setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC)
+			 | (mode ? STEREO_DAC : MONO_DAC)));
+#endif
+  dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels
+				 * changes */
+  return mode;
+}
+
+static void
+sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
+{
+  unsigned long   flags;
+
+  if (!irq_mode)
+    dsp_speaker (ON);
+
+  irq_mode = IMODE_OUTPUT;
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+  if (sound_dsp_dmachan[dev] > 3)
+    count >>= 1;
+  count--;
+
+  if (dsp_highspeed)
+    {
+      DISABLE_INTR (flags);
+      if (dsp_command (0x48))	/* High speed size */
+	{
+	  dsp_command (count & 0xff);
+	  dsp_command ((count >> 8) & 0xff);
+	  dsp_command (0x91);	/* High speed 8 bit DAC */
+	}
+      else
+	printk ("SB Error: Unable to start (high speed) DAC\n");
+      RESTORE_INTR (flags);
+    }
+  else
+    {
+      DISABLE_INTR (flags);
+      if (dsp_command (0x14))	/* 8-bit DAC (DMA) */
+	{
+	  dsp_command (count & 0xff);
+	  dsp_command ((count >> 8) & 0xff);
+	}
+      else
+	printk ("SB Error: Unable to start DAC\n");
+      RESTORE_INTR (flags);
+    }
+  intr_active = 1;
+}
+
+static void
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
+{
+  /* Start a DMA input to the buffer pointed by dmaqtail */
+
+  unsigned long   flags;
+
+  if (!irq_mode)
+    dsp_speaker (OFF);
+
+  irq_mode = IMODE_INPUT;
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+  if (sound_dsp_dmachan[dev] > 3)
+    count >>= 1;
+  count--;
+
+  if (dsp_highspeed)
+    {
+      DISABLE_INTR (flags);
+      if (dsp_command (0x48))	/* High speed size */
+	{
+	  dsp_command (count & 0xff);
+	  dsp_command ((count >> 8) & 0xff);
+	  dsp_command (0x99);	/* High speed 8 bit ADC */
+	}
+      else
+	printk ("SB Error: Unable to start (high speed) ADC\n");
+      RESTORE_INTR (flags);
+    }
+  else
+    {
+      DISABLE_INTR (flags);
+      if (dsp_command (0x24))	/* 8-bit ADC (DMA) */
+	{
+	  dsp_command (count & 0xff);
+	  dsp_command ((count >> 8) & 0xff);
+	}
+      else
+	printk ("SB Error: Unable to start ADC\n");
+      RESTORE_INTR (flags);
+    }
+
+  intr_active = 1;
+}
+
+static void
+dsp_cleanup (void)
+{
+  intr_active = 0;
+}
+
+static int
+sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+  dsp_cleanup ();
+  dsp_speaker (OFF);
+  return 0;
+}
+
+static int
+sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+  dsp_cleanup ();
+  dsp_speaker (ON);
+  return 0;
+}
+
+static void
+sb_dsp_halt_xfer (int dev)
+{
+}
+
+static int
+sb_dsp_open (int dev, int mode)
+{
+  int             retval;
+
+  if (!sb_dsp_ok)
+    {
+      printk ("SB Error: SoundBlaster board not installed\n");
+      return RET_ERROR (ENXIO);
+    }
+
+  if (!irq_ok)
+    {
+      printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq);
+      return RET_ERROR (ENXIO);
+    }
+
+  if (intr_active || (midi_busy && midi_mode == UART_MIDI))
+    {
+      printk ("SB: PCM not possible during MIDI input\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  if (mode != OPEN_READ && mode != OPEN_WRITE)
+    {
+      printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n");
+      return RET_ERROR (EINVAL);
+    }
+
+  retval = set_dsp_irq (sbc_irq);
+  if (retval)
+    return retval;
+
+  if (!DMAbuf_open_dma (dev))
+    {
+      RELEASE_IRQ (sbc_irq);
+      printk ("SB: DMA Busy\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  dsp_set_stereo (OFF);
+  dsp_speed (DSP_DEFAULT_SPEED);
+  irq_mode = IMODE_NONE;
+
+  dsp_busy = 1;
+
+  return 0;
+}
+
+static void
+sb_dsp_close (int dev)
+{
+  DMAbuf_close_dma (dev);
+  RELEASE_IRQ (sbc_irq);
+  dsp_cleanup ();
+  dsp_speed (DSP_DEFAULT_SPEED);
+  dsp_set_stereo (OFF);
+  dsp_speaker (OFF);
+  dsp_busy = 0;
+}
+
+static int
+sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+  switch (cmd)
+    {
+    case SOUND_PCM_WRITE_RATE:
+      if (local)
+	return dsp_speed (arg);
+      return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_READ_RATE:
+      if (local)
+	return dsp_current_speed;
+      return IOCTL_OUT (arg, dsp_current_speed);
+      break;
+
+    case SOUND_PCM_WRITE_CHANNELS:
+      return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+      break;
+
+    case SOUND_PCM_READ_CHANNELS:
+      if (local)
+	return dsp_stereo + 1;
+      return IOCTL_OUT (arg, dsp_stereo + 1);
+      break;
+
+    case SNDCTL_DSP_STEREO:
+      if (local)
+	return dsp_set_stereo (arg);
+      return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+      break;
+
+    case SOUND_PCM_WRITE_BITS:
+    case SOUND_PCM_READ_BITS:
+      if (local)
+	return 8;
+      return IOCTL_OUT (arg, 8);/* Only 8 bits/sample supported */
+      break;
+
+    case SOUND_PCM_WRITE_FILTER:
+    case SOUND_PCM_READ_FILTER:
+      return RET_ERROR (EINVAL);
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+  return RET_ERROR (EINVAL);
+}
+
+static void
+sb_dsp_reset (int dev)
+{
+  unsigned long   flags;
+
+  DISABLE_INTR (flags);
+
+  reset_dsp ();
+  dsp_cleanup ();
+
+  RESTORE_INTR (flags);
+}
+
+#endif
+
+int
+sb_dsp_detect (struct address_info *hw_config)
+{
+  sbc_base = hw_config->io_base;
+  sbc_irq = hw_config->irq;
+
+  if (sb_dsp_ok)
+    return 0;			/* Already initialized */
+
+  if (!reset_dsp ())
+    return 0;
+
+  return 1;			/* Detected */
+}
+
+#ifndef EXCLUDE_SBPRO
+
+static void
+setmixer (unsigned char port, unsigned char value)
+{
+  OUTB (port, MIXER_ADDR);	/* Select register */
+  tenmicrosec ();
+  OUTB (value, MIXER_DATA);
+  tenmicrosec ();
+}
+
+static int
+getmixer (unsigned char port)
+{
+  int             val;
+
+  OUTB (port, MIXER_ADDR);	/* Select register */
+  tenmicrosec ();
+  val = INB (MIXER_DATA);
+  tenmicrosec ();
+
+  return val;
+}
+
+static int
+detect_mixer (void)
+{
+  /*
+   * Detect the mixer by changing parameters of two volume channels. If the
+   * values read back match with the values written, the mixer is there (is
+   * it?)
+   */
+  setmixer (FM_VOL, 0xff);
+  setmixer (VOC_VOL, 0x33);
+
+  if (getmixer (FM_VOL) != 0xff)
+    return 0;			/* No match */
+  if (getmixer (VOC_VOL) != 0x33)
+    return 0;
+
+  return 1;
+}
+
+static void
+init_mixer (void)
+{
+  setmixer (MASTER_VOL, 0xbb);
+  setmixer (VOC_VOL, 0x99);
+  setmixer (LINE_VOL, 0xbb);
+  setmixer (FM_VOL, 0x99);
+  setmixer (CD_VOL, 0x11);
+  setmixer (MIC_MIX, 0x11);
+  setmixer (RECORD_SRC, 0x31);
+  setmixer (OUT_FILTER, 0x31);
+}
+
+static void
+set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output)
+{
+  setmixer (RECORD_SRC, (record_source
+			 | (hifreq_filter ? FREQ_HI : FREQ_LOW)
+			 | (filter_input ? FILT_ON : FILT_OFF)));
+
+  setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC)
+			 | (filter_output ? FILT_ON : FILT_OFF)));
+
+  hi_filter = hifreq_filter;
+  filter_in = filter_input;
+  filter_out = filter_output;
+}
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int device)
+{
+  int             left = ((left_vol * div) + 50) / 100;
+  int             right = ((right_vol * div) + 50) / 100;
+
+  setmixer (device, ((left & 0xf) << 4) | (right & 0xf));
+
+  return (left_vol | (right_vol << 8));
+}
+
+static int
+sbp_mixer_set (int whichDev, unsigned int level)
+{
+  int             left, right, devmask;
+
+  left = level & 0x7f;
+  right = (level & 0x7f00) >> 8;
+
+  switch (whichDev)
+    {
+    case SOUND_MIXER_VOLUME:	/* Master volume (0-15) */
+      return mixer_output (right, left, 15, MASTER_VOL);
+      break;
+    case SOUND_MIXER_SYNTH:	/* Internal synthesizer (0-15) */
+      return mixer_output (right, left, 15, FM_VOL);
+      break;
+    case SOUND_MIXER_PCM:	/* PAS PCM (0-15) */
+      return mixer_output (right, left, 15, VOC_VOL);
+      break;
+    case SOUND_MIXER_LINE:	/* External line (0-15) */
+      return mixer_output (right, left, 15, LINE_VOL);
+      break;
+    case SOUND_MIXER_CD:	/* CD (0-15) */
+      return mixer_output (right, left, 15, CD_VOL);
+      break;
+    case SOUND_MIXER_MIC:	/* External microphone (0-7) */
+      return mixer_output (right, left, 7, MIC_VOL);
+      break;
+
+    case SOUND_MIXER_RECSRC:
+      devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+      if (devmask != SOUND_MASK_MIC &&
+	  devmask != SOUND_MASK_LINE &&
+	  devmask != SOUND_MASK_CD)
+	{			/* More than one devices selected. Drop the
+				 * previous selection */
+	  devmask &= ~rec_devices;
+	}
+
+      if (devmask != SOUND_MASK_MIC &&
+	  devmask != SOUND_MASK_LINE &&
+	  devmask != SOUND_MASK_CD)
+	{			/* More than one devices selected. Default to
+				 * mic */
+	  devmask = SOUND_MASK_MIC;
+	}
+
+      if (devmask ^ rec_devices)/* Input source changed */
+	{
+	  switch (devmask)
+	    {
+
+	    case SOUND_MASK_MIC:
+	      set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
+	      break;
+
+	    case SOUND_MASK_LINE:
+	      set_filter (SRC_LINE, hi_filter, filter_in, filter_out);
+	      break;
+
+	    case SOUND_MASK_CD:
+	      set_filter (SRC_CD, hi_filter, filter_in, filter_out);
+	      break;
+
+	    default:
+	      set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
+	    }
+	}
+
+      rec_devices = devmask;
+
+      return rec_devices;
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+}
+
+static int
+mixer_input (int div, int device)
+{
+  int             level, left, right, half;
+
+  level = getmixer (device);
+  half = div / 2;
+
+  left = ((((level & 0xf0) >> 4) * 100) + half) / div;
+  right = (((level & 0x0f) * 100) + half) / div;
+
+  return (right << 8) | left;
+}
+
+static int
+sbp_mixer_get (int whichDev)
+{
+
+  switch (whichDev)
+    {
+    case SOUND_MIXER_VOLUME:	/* Master volume (0-15) */
+      return mixer_input (15, MASTER_VOL);
+      break;
+    case SOUND_MIXER_SYNTH:	/* Internal synthesizer (0-15) */
+      return mixer_input (15, FM_VOL);
+      break;
+    case SOUND_MIXER_PCM:	/* PAS PCM (0-15) */
+      return mixer_input (15, VOC_VOL);
+      break;
+    case SOUND_MIXER_LINE:	/* External line (0-15) */
+      return mixer_input (15, LINE_VOL);
+      break;
+    case SOUND_MIXER_CD:	/* CD (0-15) */
+      return mixer_input (15, CD_VOL);
+      break;
+    case SOUND_MIXER_MIC:	/* External microphone (0-7) */
+      return mixer_input (7, MIC_VOL);
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+}
+
+/*
+ * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See
+ * sbinfo.doc for details on granularity and such. Basically, the mixer
+ * forces the lowest bit high, effectively reducing the possible settings by
+ * one half.  Yes, that's right, volume levels have 8 settings, and
+ * microphone has four.  Sucks.
+ */
+static int
+mixer_set_levels (struct sb_mixer_levels *user_l)
+{
+  struct sb_mixer_levels l;
+
+  IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l));
+
+  if (l.master.l & ~0xF || l.master.r & ~0xF
+      || l.line.l & ~0xF || l.line.r & ~0xF
+      || l.voc.l & ~0xF || l.voc.r & ~0xF
+      || l.fm.l & ~0xF || l.fm.r & ~0xF
+      || l.cd.l & ~0xF || l.cd.r & ~0xF
+      || l.mic & ~0x7)
+    return (RET_ERROR (EINVAL));
+
+  setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r);
+  setmixer (LINE_VOL, (l.line.l << 4) | l.line.r);
+  setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r);
+  setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r);
+  setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r);
+  setmixer (MIC_VOL, l.mic);
+  return (0);
+}
+
+/*
+ * This sets aspects of the Mixer that are not volume levels. (Recording
+ * source, filter level, I/O filtering, and stereo.)
+ */
+
+static int
+mixer_set_params (struct sb_mixer_params *user_p)
+{
+  struct sb_mixer_params p;
+
+  IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
+
+  if (p.record_source != SRC_MIC
+      && p.record_source != SRC_CD
+      && p.record_source != SRC_LINE)
+    return (EINVAL);
+
+  /*
+   * I'm not sure if this is The Right Thing.  Should stereo be entirely
+   * under control of DSP?  I like being able to toggle it while a sound is
+   * playing, so I do this... because I can.
+   */
+
+  dsp_stereo = !!p.dsp_stereo;
+
+  set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output);
+
+  switch (p.record_source)
+    {
+
+    case SRC_MIC:
+      rec_devices = SOUND_MASK_MIC;
+      break;
+
+    case SRC_LINE:
+      rec_devices = SOUND_MASK_LINE;
+      break;
+
+    case SRC_CD:
+      rec_devices = SOUND_MASK_CD;
+    }
+
+  return (0);
+}
+
+/* Read the current mixer level settings into the user's struct. */
+static int
+mixer_get_levels (struct sb_mixer_levels *user_l)
+{
+  S_BYTE          val;
+  struct sb_mixer_levels l;
+
+  val = getmixer (MASTER_VOL);	/* Master */
+  l.master.l = B4 (val >> 4);
+  l.master.r = B4 (val);
+
+  val = getmixer (LINE_VOL);	/* FM */
+  l.line.l = B4 (val >> 4);
+  l.line.r = B4 (val);
+
+  val = getmixer (VOC_VOL);	/* DAC */
+  l.voc.l = B4 (val >> 4);
+  l.voc.r = B4 (val);
+
+  val = getmixer (FM_VOL);	/* FM */
+  l.fm.l = B4 (val >> 4);
+  l.fm.r = B4 (val);
+
+  val = getmixer (CD_VOL);	/* CD */
+  l.cd.l = B4 (val >> 4);
+  l.cd.r = B4 (val);
+
+  val = getmixer (MIC_VOL);	/* Microphone */
+  l.mic = B3 (val);
+
+  IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
+
+  return (0);
+}
+
+/* Read the current mixer parameters into the user's struct. */
+static int
+mixer_get_params (struct sb_mixer_params *user_params)
+{
+  S_BYTE          val;
+  struct sb_mixer_params params;
+
+  val = getmixer (RECORD_SRC);
+  params.record_source = val & 0x07;
+  params.hifreq_filter = !!(val & FREQ_HI);
+  params.filter_input = (val & FILT_OFF) ? OFF : ON;
+  params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON;
+  params.dsp_stereo = dsp_stereo;
+
+  IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
+  return (0);
+}
+
+static int
+sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+  if (((cmd >> 8) & 0xff) == 'M')
+    {
+      if (cmd & IOC_IN)
+	return IOCTL_OUT (arg, sbp_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+      else
+	{			/* Read parameters */
+
+	  switch (cmd & 0xff)
+	    {
+
+	    case SOUND_MIXER_RECSRC:
+	      return IOCTL_OUT (arg, rec_devices);
+	      break;
+
+	    case SOUND_MIXER_DEVMASK:
+	      return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+	      break;
+
+	    case SOUND_MIXER_STEREODEVS:
+	      return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC);
+	      break;
+
+	    case SOUND_MIXER_RECMASK:
+	      return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES);
+	      break;
+
+	    case SOUND_MIXER_CAPS:
+	      return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT);
+	      break;
+
+	    default:
+	      return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff));
+	    }
+	}
+    }
+  else
+    {
+      switch (cmd)
+	{
+	case MIXER_IOCTL_SET_LEVELS:
+	  return (mixer_set_levels ((struct sb_mixer_levels *) arg));
+	case MIXER_IOCTL_SET_PARAMS:
+	  return (mixer_set_params ((struct sb_mixer_params *) arg));
+	case MIXER_IOCTL_READ_LEVELS:
+	  return (mixer_get_levels ((struct sb_mixer_levels *) arg));
+	case MIXER_IOCTL_READ_PARAMS:
+	  return (mixer_get_params ((struct sb_mixer_params *) arg));
+	case MIXER_IOCTL_RESET:
+	  init_mixer ();
+	  return (0);
+	default:
+	  return RET_ERROR (EINVAL);
+	}
+    }
+}
+
+/* End of mixer code */
+#endif
+
+#ifndef EXCLUDE_MIDI
+
+/* Midi code */
+
+static int
+sb_midi_open (int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+  int             ret;
+
+  if (!sb_dsp_ok)
+    {
+      printk ("SB Error: MIDI hardware not installed\n");
+      return RET_ERROR (ENXIO);
+    }
+
+  if (mode != OPEN_WRITE && !duplex_midi)
+    {
+      if (num_midis == 1)
+	printk ("SoundBlaster: Midi input not currently supported\n");
+      return RET_ERROR (EPERM);
+    }
+
+  midi_mode = NORMAL_MIDI;
+  if (mode != OPEN_WRITE)
+    {
+      if (dsp_busy || intr_active)
+	return RET_ERROR (EBUSY);
+      midi_mode = UART_MIDI;
+    }
+
+  if (dsp_highspeed || dsp_stereo)
+    {
+      printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
+      return RET_ERROR (EBUSY);
+    }
+
+  if (midi_mode == UART_MIDI)
+    {
+      irq_mode = IMODE_MIDI;
+
+      reset_dsp ();
+      dsp_speaker (OFF);
+
+      if (!dsp_command (0x35))
+	return RET_ERROR (EIO);	/* Enter the UART mode */
+      intr_active = 1;
+
+      if ((ret = set_dsp_irq (sbc_irq)) < 0)
+	{
+	  reset_dsp ();
+	  return 0;		/* IRQ not free */
+	}
+    }
+
+  midi_busy = 1;
+
+  return 0;
+}
+
+static void
+sb_midi_close (int dev)
+{
+  if (midi_mode == UART_MIDI)
+    {
+      reset_dsp ();		/* The only way to kill the UART mode */
+      RELEASE_IRQ (sbc_irq);
+    }
+  intr_active = 0;
+  midi_busy = 0;
+}
+
+static int
+sb_midi_out (int dev, unsigned char midi_byte)
+{
+  unsigned long   flags;
+
+  midi_busy = 1;		/* Kill all notes after close */
+
+  if (midi_mode == NORMAL_MIDI)
+    {
+      DISABLE_INTR (flags);
+      if (dsp_command (0x38))
+	dsp_command (midi_byte);
+      else
+	printk ("SB Error: Unable to send a MIDI byte\n");
+      RESTORE_INTR (flags);
+    }
+  else
+    dsp_command (midi_byte);	/* UART write */
+
+  return 1;
+}
+
+static int
+sb_midi_start_read (int dev)
+{
+  if (midi_mode != UART_MIDI)
+    {
+      printk ("SoundBlaster: MIDI input not implemented.\n");
+      return RET_ERROR (EPERM);
+    }
+  return 0;
+}
+
+static int
+sb_midi_end_read (int dev)
+{
+  if (midi_mode == UART_MIDI)
+    {
+      reset_dsp ();
+      intr_active = 0;
+    }
+  return 0;
+}
+
+static int
+sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+  return RET_ERROR (EPERM);
+}
+
+/* End of midi code */
+#endif
+
+#ifndef EXCLUDE_AUDIO
+static struct audio_operations sb_dsp_operations =
+{
+  "SoundBlaster",
+  sb_dsp_open,
+  sb_dsp_close,
+  sb_dsp_output_block,
+  sb_dsp_start_input,
+  sb_dsp_ioctl,
+  sb_dsp_prepare_for_input,
+  sb_dsp_prepare_for_output,
+  sb_dsp_reset,
+  sb_dsp_halt_xfer,
+  NULL,				/* has_output_drained */
+  NULL				/* copy_from_user */
+};
+
+#endif
+
+#ifndef EXCLUDE_SBPRO
+static struct mixer_operations sb_mixer_operations =
+{
+  sb_mixer_ioctl
+};
+
+#endif
+
+#ifndef EXCLUDE_MIDI
+static struct midi_operations sb_midi_operations =
+{
+  {"SoundBlaster", 0},
+  sb_midi_open,
+  sb_midi_close,
+  sb_midi_ioctl,
+  sb_midi_out,
+  sb_midi_start_read,
+  sb_midi_end_read,
+  NULL,				/* Kick */
+  NULL,				/* command */
+  NULL				/* buffer_status */
+};
+
+#endif
+
+static int
+verify_irq (void)
+{
+#if 0
+  unsigned long   loop;
+
+  irq_ok = 0;
+
+  if (set_dsp_irq (sbc_irq) == -1)
+    {
+      printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+      return 0;
+    }
+
+
+  irq_mode = IMODE_INIT;
+
+  dsp_command (0xf2);		/* This should cause immediate interrupt */
+
+  for (loop = 100000; loop > 0 && !irq_ok; loop--);
+
+  RELEASE_IRQ (sbc_irq);
+
+  if (!irq_ok)
+    {
+      printk ("SB Warning: IRQ test not passed!");
+      irq_ok = 1;
+    }
+#else
+  irq_ok = 1;
+#endif
+  return irq_ok;
+}
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+  int             i, major, minor;
+
+  major = minor = 0;
+  dsp_command (0xe1);		/* Get version */
+
+  for (i = 1000; i; i--)
+    {
+      if (inb (DSP_DATA_AVAIL) & 0x80)
+	{			/* wait for Data Ready */
+	  if (major == 0)
+	    major = inb (DSP_READ);
+	  else
+	    {
+	      minor = inb (DSP_READ);
+	      break;
+	    }
+	}
+    }
+
+#ifndef EXCLUDE_SBPRO
+  if (detect_mixer ())
+    {
+#ifndef SCO
+      sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor);
+#endif
+      init_mixer ();
+      mixer_devs[num_mixers++] = &sb_mixer_operations;
+
+      if (major == 2 || major == 3)
+	duplex_midi = 1;
+
+      if (major == 4)
+	sb16 = 1;
+
+      if (major >= 3)
+	dsp_model = 2;
+
+#ifndef EXCLUDE_YM8312
+      if (major > 3 || (major == 3 && minor > 0))	/* SB Pro2 or later */
+	{
+	  enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+	}
+#endif
+    }
+  else
+#endif
+
+#ifndef SCO
+    sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor);
+#endif
+
+  printk (" <%s>", sb_dsp_operations.name);
+
+  if (!verify_irq ())
+    return mem_start;
+
+#ifndef EXCLUDE_AUDIO
+  if (num_dspdevs < MAX_DSP_DEV)
+    {
+      dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
+      sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+      sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+      sound_dsp_dmachan[my_dev] = hw_config->dma;
+      sound_dma_automode[my_dev] = 0;
+    }
+  else
+    printk ("SB: Too many DSP devices available\n");
+#endif
+
+#ifndef EXCLUDE_MIDI
+  if (!midi_disabled)		/* Midi don't work in the SB emulation mode
+				 * of PAS */
+    midi_devs[num_midis++] = &sb_midi_operations;
+#endif
+
+  sb_dsp_ok = 1;
+  return mem_start;
+}
+
+void
+sb_dsp_disable_midi (void)
+{
+  midi_disabled = 1;
+}
+
+#endif
diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c
new file mode 100644
index 0000000..4901ea9
--- /dev/null
+++ b/drivers/sound/sequencer.c
@@ -0,0 +1,1147 @@
+/*
+ * linux/kernel/chr_drv/sound/sequencer.c
+ * 
+ * The sequencer personality manager.
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static int      sequencer_ok = 0;
+
+DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+/* DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); */
+#define midi_sleeper seq_sleeper
+#define midi_sleep_flag seq_sleep_flag
+
+static int      midi_opened[MAX_MIDI_DEV] =
+{0};				/* 1 if the process has opened MIDI */
+static int      midi_written[MAX_MIDI_DEV] =
+{0};
+
+long            seq_time = 0;	/* Reference point for the timer */
+
+#include "tuning.h"
+
+#define EV_SZ	8
+static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ];
+static unsigned char iqueue[SEQ_MAX_QUEUE][4];
+static volatile int qhead = 0, qtail = 0, qlen = 0;
+static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
+static volatile int seq_playing = 0;
+static int      sequencer_busy = 0;
+static int      output_treshold;
+static unsigned synth_open_mask;
+
+static int      seq_queue (unsigned char *note);
+static void     seq_startplay (void);
+static int      seq_sync (void);
+static void     seq_reset (void);
+static int      pmgr_present[MAX_SYNTH_DEV] =
+{0};
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices
+#endif
+
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  int             c = count, p = 0;
+
+  dev = dev >> 4;
+
+  if (dev)			/* Patch manager device */
+    return pmgr_read (dev - 1, file, buf, count);
+
+  while (c > 3)
+    {
+      if (!iqlen)
+	{
+	  DO_SLEEP (midi_sleeper, midi_sleep_flag, 0);
+
+	  if (!iqlen)
+	    return count - c;
+	}
+
+      COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4);
+      p += 4;
+      c -= 4;
+
+      iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+      iqlen--;
+    }
+
+  return count - c;
+}
+
+static void
+sequencer_midi_output (int dev)
+{
+  /* Currently NOP */
+}
+
+static void
+copy_to_input (unsigned char *event)
+{
+  unsigned long   flags;
+
+  if (iqlen >= (SEQ_MAX_QUEUE - 1))
+    return;			/* Overflow */
+
+  memcpy (iqueue[iqtail], event, 4);
+  iqlen++;
+  iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+
+  DISABLE_INTR (flags);
+  if (SOMEONE_WAITING (midi_sleep_flag))
+    {
+      WAKE_UP (midi_sleeper, midi_sleep_flag);
+    }
+  RESTORE_INTR (flags);
+}
+
+static void
+sequencer_midi_input (int dev, unsigned char data)
+{
+  int             tstamp;
+  unsigned char   event[4];
+
+  if (data == 0xfe)		/* Active sensing */
+    return;			/* Ignore */
+
+  tstamp = GET_TIME () - seq_time;	/* Time since open() */
+  tstamp = (tstamp << 8) | SEQ_WAIT;
+
+  copy_to_input ((unsigned char *) &tstamp);
+
+  event[0] = SEQ_MIDIPUTC;
+  event[1] = data;
+  event[2] = dev;
+  event[3] = 0;
+
+  copy_to_input (event);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  unsigned char   event[EV_SZ], ev_code;
+  int             p = 0, c, ev_size;
+  int             err;
+  int             mode = file->mode & O_ACCMODE;
+
+  dev = dev >> 4;
+
+  DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+  if (mode == OPEN_READ)
+    return RET_ERROR (EIO);
+
+  if (dev)			/* Patch manager device */
+    return pmgr_write (dev - 1, file, buf, count);
+
+  c = count;
+
+  while (c >= 4)
+    {
+      COPY_FROM_USER (event, buf, p, 4);
+      ev_code = event[0];
+
+      if (ev_code == SEQ_FULLSIZE)
+	{
+	  int             err;
+
+	  dev = *(unsigned short *) &event[2];
+	  if (dev < 0 || dev >= num_synths)
+	    return RET_ERROR (ENXIO);
+
+	  if (!(synth_open_mask & (1 << dev)))
+	    return RET_ERROR (ENXIO);
+
+	  err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0);
+	  if (err < 0)
+	    return err;
+
+	  return err;
+	}
+
+      if (ev_code == SEQ_EXTENDED || ev_code == SEQ_PRIVATE)
+	{
+
+	  ev_size = 8;
+
+	  if (c < ev_size)
+	    {
+	      if (!seq_playing)
+		seq_startplay ();
+	      return count - c;
+	    }
+
+	  COPY_FROM_USER (&event[4], buf, p + 4, 4);
+
+	}
+      else
+	ev_size = 4;
+
+      if (event[0] == SEQ_MIDIPUTC)
+	{
+
+	  if (!midi_opened[event[2]])
+	    {
+	      int             mode;
+	      int             dev = event[2];
+
+	      if (dev >= num_midis)
+		{
+		  printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev);
+		  return RET_ERROR (ENXIO);
+		}
+
+	      mode = file->mode & O_ACCMODE;
+
+	      if ((err = midi_devs[dev]->open (dev, mode,
+			  sequencer_midi_input, sequencer_midi_output)) < 0)
+		{
+		  seq_reset ();
+		  printk ("Sequencer Error: Unable to open Midi #%d\n", dev);
+		  return err;
+		}
+
+	      midi_opened[dev] = 1;
+	    }
+
+	}
+
+      if (!seq_queue (event))
+	{
+
+	  if (!seq_playing)
+	    seq_startplay ();
+	  return count - c;
+	}
+
+      p += ev_size;
+      c -= ev_size;
+    }
+
+  if (!seq_playing)
+    seq_startplay ();
+
+  return count;
+}
+
+static int
+seq_queue (unsigned char *note)
+{
+
+  /* Test if there is space in the queue */
+
+  if (qlen >= SEQ_MAX_QUEUE)
+    if (!seq_playing)
+      seq_startplay ();		/* Give chance to drain the queue */
+
+  if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleep_flag))
+    {
+      /* Sleep until there is enough space on the queue */
+      DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+    }
+
+  if (qlen >= SEQ_MAX_QUEUE)
+    return 0;			/* To be sure */
+
+  memcpy (&queue[qtail][0], note, EV_SZ);
+
+  qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+  qlen++;
+
+  return 1;
+}
+
+static int
+extended_event (unsigned char *q)
+{
+  int             dev = q[2];
+
+  if (dev < 0 || dev >= num_synths)
+    return RET_ERROR (ENXIO);
+
+  if (!(synth_open_mask & (1 << dev)))
+    return RET_ERROR (ENXIO);
+
+  switch (q[1])
+    {
+    case SEQ_NOTEOFF:
+      synth_devs[dev]->kill_note (dev, q[3], q[5]);
+      break;
+
+    case SEQ_NOTEON:
+      if (q[4] > 127 && q[4] != 255)
+	return 0;
+
+      synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
+      break;
+
+    case SEQ_PGMCHANGE:
+      synth_devs[dev]->set_instr (dev, q[3], q[4]);
+      break;
+
+    case SEQ_AFTERTOUCH:
+      synth_devs[dev]->aftertouch (dev, q[3], q[4]);
+      break;
+
+    case SEQ_BALANCE:
+      synth_devs[dev]->panning (dev, q[3], (char) q[4]);
+      break;
+
+    case SEQ_CONTROLLER:
+      synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]);
+      break;
+
+    default:
+      return RET_ERROR (EINVAL);
+    }
+
+  return 0;
+}
+
+static void
+seq_startplay (void)
+{
+  int             this_one;
+  unsigned long  *delay;
+  unsigned char  *q;
+
+  while (qlen > 0)
+    {
+      qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+      qlen--;
+
+      q = &queue[this_one][0];
+
+      switch (q[0])
+	{
+	case SEQ_NOTEOFF:
+	  if (synth_open_mask & (1 << 0))
+	    if (synth_devs[0])
+	      synth_devs[0]->kill_note (0, q[1], q[3]);
+	  break;
+
+	case SEQ_NOTEON:
+	  if (q[4] < 128 || q[4] == 255)
+	    if (synth_open_mask & (1 << 0))
+	      if (synth_devs[0])
+		synth_devs[0]->start_note (0, q[1], q[2], q[3]);
+	  break;
+
+	case SEQ_WAIT:
+	  delay = (unsigned long *) q;	/* Bytes 1 to 3 are containing the
+					 * delay in GET_TIME() */
+	  *delay = (*delay >> 8) & 0xffffff;
+
+	  if (*delay > 0)
+	    {
+	      long            time;
+
+	      seq_playing = 1;
+	      time = *delay;
+
+	      request_sound_timer (time);
+
+	      if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+		{
+		  unsigned long   flags;
+
+		  DISABLE_INTR (flags);
+		  if (SOMEONE_WAITING (seq_sleep_flag))
+		    {
+		      WAKE_UP (seq_sleeper, seq_sleep_flag);
+		    }
+		  RESTORE_INTR (flags);
+		}
+	      return;		/* Stop here. Timer routine will continue
+				 * playing after the delay */
+	    }
+	  break;
+
+	case SEQ_PGMCHANGE:
+	  if (synth_open_mask & (1 << 0))
+	    if (synth_devs[0])
+	      synth_devs[0]->set_instr (0, q[1], q[2]);
+	  break;
+
+	case SEQ_SYNCTIMER:	/* Reset timer */
+	  seq_time = GET_TIME ();
+	  break;
+
+	case SEQ_MIDIPUTC:	/* Put a midi character */
+	  if (midi_opened[q[2]])
+	    {
+	      int             dev;
+
+	      dev = q[2];
+
+	      if (!midi_devs[dev]->putc (dev, q[1]))
+		{
+		  /*
+		   * Output FIFO is full. Wait one timer cycle and try again.
+		   */
+
+		  qlen++;
+		  qhead = this_one;	/* Restore queue */
+		  seq_playing = 1;
+		  request_sound_timer (-1);
+		  return;
+		}
+	      else
+		midi_written[dev] = 1;
+	    }
+	  break;
+
+	case SEQ_ECHO:
+	  copy_to_input (q);	/* Echo back to the process */
+	  break;
+
+	case SEQ_PRIVATE:
+	  if (q[1] < num_synths)
+	    synth_devs[q[1]]->hw_control (q[1], q);
+	  break;
+
+	case SEQ_EXTENDED:
+	  extended_event (q);
+	  break;
+
+	default:;
+	}
+
+    }
+
+  seq_playing = 0;
+
+  if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+    {
+      unsigned long   flags;
+
+      DISABLE_INTR (flags);
+      if (SOMEONE_WAITING (seq_sleep_flag))
+	{
+	  WAKE_UP (seq_sleeper, seq_sleep_flag);
+	}
+      RESTORE_INTR (flags);
+    }
+
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+  {
+    int             retval, mode, i;
+
+    dev = dev >> 4;
+    mode = file->mode & O_ACCMODE;
+
+    DEB (printk ("sequencer_open(dev=%d)\n", dev));
+
+    if (!sequencer_ok)
+      {
+	printk ("Soundcard: Sequencer not initialized\n");
+	return RET_ERROR (ENXIO);
+      }
+
+    if (dev)			/* Patch manager device */
+      {
+	int             err;
+
+	dev--;
+	if (pmgr_present[dev])
+	  return RET_ERROR (EBUSY);
+	if ((err = pmgr_open (dev)) < 0)
+	  return err;		/* Failed */
+
+	pmgr_present[dev] = 1;
+	return err;
+      }
+
+    if (sequencer_busy)
+      {
+	printk ("Sequencer busy\n");
+	return RET_ERROR (EBUSY);
+      }
+
+    if (!(num_synths + num_midis))
+      return RET_ERROR (ENXIO);
+
+    synth_open_mask = 0;
+
+    if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+      for (i = 0; i < num_synths; i++)	/* Open synth devices */
+	if (synth_devs[i]->open (i, mode) < 0)
+	  printk ("Sequencer: Warning! Cannot open synth device #%d\n", i);
+	else
+	  synth_open_mask |= (1 << i);
+
+    seq_time = GET_TIME ();
+
+    for (i = 0; i < num_midis; i++)
+      {
+	midi_opened[i] = 0;
+	midi_written[i] = 0;
+      }
+
+    if (mode == OPEN_READ || mode == OPEN_READWRITE)
+      {				/* Initialize midi input devices */
+	if (!num_midis)
+	  {
+	    printk ("Sequencer: No Midi devices. Input not possible\n");
+	    return RET_ERROR (ENXIO);
+	  }
+
+	for (i = 0; i < num_midis; i++)
+	  {
+	    if ((retval = midi_devs[i]->open (i, mode,
+			 sequencer_midi_input, sequencer_midi_output)) >= 0)
+	      midi_opened[i] = 1;
+	  }
+      }
+
+    sequencer_busy = 1;
+    RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+    RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+    output_treshold = SEQ_MAX_QUEUE / 2;
+
+    for (i = 0; i < num_synths; i++)
+      if (pmgr_present[i])
+	pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0);
+
+    return 0;
+  }
+
+void
+seq_drain_midi_queues (void)
+{
+  int             i, n;
+
+  /*
+   * Give the Midi drivers time to drain their output queues
+   */
+
+  n = 1;
+
+  while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n)
+    {
+      n = 0;
+
+      for (i = 0; i < num_midis; i++)
+	if (midi_opened[i] && midi_written[i])
+	  if (midi_devs[i]->buffer_status != NULL)
+	    if (midi_devs[i]->buffer_status (i))
+	      n++;
+
+      /*
+       * Let's have a delay
+       */
+      if (n)
+	{
+	  DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10);
+	}
+    }
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+  {
+    int             i;
+    int             mode = file->mode & O_ACCMODE;
+
+    dev = dev >> 4;
+
+    DEB (printk ("sequencer_release(dev=%d)\n", dev));
+
+    if (dev)			/* Patch manager device */
+      {
+	dev--;
+	pmgr_release (dev);
+	pmgr_present[dev] = 0;
+	return;
+      }
+
+    /*
+     * Wait until the queue is empty
+     */
+
+    while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen)
+      {
+	seq_sync ();
+      }
+
+    if (mode != OPEN_READ)
+      seq_drain_midi_queues ();	/* Ensure the output queues are empty */
+    seq_reset ();
+    if (mode != OPEN_READ)
+      seq_drain_midi_queues ();	/* Flush the all notes off messages */
+
+    for (i = 0; i < num_midis; i++)
+      if (midi_opened[i])
+	midi_devs[i]->close (i);
+
+    if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+      for (i = 0; i < num_synths; i++)
+	if (synth_open_mask & (1 << i))	/* Actually opened */
+	  if (synth_devs[i])
+	    synth_devs[i]->close (i);
+
+    for (i = 0; i < num_synths; i++)
+      if (pmgr_present[i])
+	pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+    sequencer_busy = 0;
+  }
+
+static int
+seq_sync (void)
+{
+  if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+    seq_startplay ();
+
+  if (qlen && !SOMEONE_WAITING (seq_sleep_flag))	/* Queue not empty */
+    {
+      DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+    }
+
+  return qlen;
+}
+
+static void
+midi_outc (int dev, unsigned char data)
+{
+  /*
+   * NOTE! Calls sleep(). Don't call this from interrupt.
+   */
+
+  int             n;
+
+  /* This routine sends one byte to the Midi channel. */
+  /* If the output Fifo is full, it waits until there */
+  /* is space in the queue */
+
+  n = 300;			/* Timeout in jiffies */
+
+  while (n && !midi_devs[dev]->putc (dev, data))
+    {
+      DO_SLEEP (seq_sleeper, seq_sleep_flag, 4);
+      n--;
+    }
+}
+
+static void
+seq_reset (void)
+{
+  /*
+   * NOTE! Calls sleep(). Don't call this from interrupt.
+   */
+
+  int             i, chn;
+
+  sound_stop_timer ();
+
+  qlen = qhead = qtail = 0;
+  iqlen = iqhead = iqtail = 0;
+
+  for (i = 0; i < num_synths; i++)
+    if (synth_open_mask & (1 << i))
+      if (synth_devs[i])
+	synth_devs[i]->reset (i);
+
+  for (i = 0; i < num_midis; i++)
+    if (midi_written[i])	/* Midi used. Some notes may still be playing */
+      {
+	for (chn = 0; chn < 16; chn++)
+	  {
+	    midi_outc (i, 0xb0 + chn);	/* Channel message */
+	    midi_outc (i, 0x7b);/* All notes off */
+	    midi_outc (i, 0);	/* Dummy parameter */
+	  }
+
+	midi_devs[i]->close (i);
+
+	midi_written[i] = 0;
+	midi_opened[i] = 0;
+      }
+
+  seq_playing = 0;
+
+  if (SOMEONE_WAITING (seq_sleep_flag))
+    printk ("Sequencer Warning: Unexpected sleeping process\n");
+
+}
+
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+		 unsigned int cmd, unsigned int arg)
+{
+  int             midi_dev, orig_dev;
+  int             mode = file->mode & O_ACCMODE;
+
+  orig_dev = dev = dev >> 4;
+
+  switch (cmd)
+    {
+
+    case SNDCTL_SEQ_SYNC:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      if (mode == OPEN_READ)
+	return 0;
+      while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+	seq_sync ();
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_RESET:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      seq_reset ();
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_TESTMIDI:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      midi_dev = IOCTL_IN (arg);
+      if (midi_dev >= num_midis)
+	return RET_ERROR (ENXIO);
+
+      if (!midi_opened[midi_dev])
+	{
+	  int             err, mode;
+
+	  mode = file->mode & O_ACCMODE;
+	  if ((err = midi_devs[midi_dev]->open (midi_dev, mode,
+						sequencer_midi_input,
+						sequencer_midi_output)) < 0)
+	    return err;
+	}
+
+      midi_opened[midi_dev] = 1;
+
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_GETINCOUNT:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      if (mode == OPEN_WRITE)
+	return 0;
+      return IOCTL_OUT (arg, iqlen);
+      break;
+
+    case SNDCTL_SEQ_GETOUTCOUNT:
+
+      if (mode == OPEN_READ)
+	return 0;
+      return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen);
+      break;
+
+    case SNDCTL_SEQ_CTRLRATE:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      /* If *arg == 0, just return the current rate */
+      return IOCTL_OUT (arg, HZ);
+      break;
+
+    case SNDCTL_SEQ_RESETSAMPLES:
+      dev = IOCTL_IN (arg);
+      if (dev < 0 || dev >= num_synths)
+	return RET_ERROR (ENXIO);
+
+      if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+	return RET_ERROR (EBUSY);
+
+      if (!orig_dev && pmgr_present[dev])
+	pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0);
+
+      return synth_devs[dev]->ioctl (dev, cmd, arg);
+      break;
+
+    case SNDCTL_SEQ_NRSYNTHS:
+      return IOCTL_OUT (arg, num_synths);
+      break;
+
+    case SNDCTL_SEQ_NRMIDIS:
+      return IOCTL_OUT (arg, num_midis);
+      break;
+
+    case SNDCTL_SYNTH_MEMAVL:
+      {
+	int             dev = IOCTL_IN (arg);
+
+	if (dev < 0 || dev >= num_synths)
+	  return RET_ERROR (ENXIO);
+
+	if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+	  return RET_ERROR (EBUSY);
+
+	return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+      }
+      break;
+
+    case SNDCTL_FM_4OP_ENABLE:
+      {
+	int             dev = IOCTL_IN (arg);
+
+	if (dev < 0 || dev >= num_synths)
+	  return RET_ERROR (ENXIO);
+
+	if (!(synth_open_mask & (1 << dev)))
+	  return RET_ERROR (ENXIO);
+
+	synth_devs[dev]->ioctl (dev, cmd, arg);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_SYNTH_INFO:
+      {
+	struct synth_info inf;
+	int             dev;
+
+	IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+	dev = inf.device;
+
+	if (dev < 0 || dev >= num_synths)
+	  return RET_ERROR (ENXIO);
+
+	if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+	  return RET_ERROR (EBUSY);
+
+	return synth_devs[dev]->ioctl (dev, cmd, arg);
+      }
+      break;
+
+    case SNDCTL_MIDI_INFO:
+      {
+	struct midi_info inf;
+	int             dev;
+
+	IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+	dev = inf.device;
+
+	if (dev < 0 || dev >= num_midis)
+	  return RET_ERROR (ENXIO);
+
+	IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf));
+	return 0;
+      }
+      break;
+
+    case SNDCTL_PMGR_IFACE:
+      {
+	struct patmgr_info *inf;
+	int             dev, err;
+
+	inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+	IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+	dev = inf->device;
+
+	if (dev < 0 || dev >= num_synths)
+	  {
+	    KERNEL_FREE (inf);
+	    return RET_ERROR (ENXIO);
+	  }
+
+	if (!synth_devs[dev]->pmgr_interface)
+	  {
+	    KERNEL_FREE (inf);
+	    return RET_ERROR (ENXIO);
+	  }
+
+	if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1)
+	  {
+	    KERNEL_FREE (inf);
+	    return err;
+	  }
+
+	IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+	KERNEL_FREE (inf);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_PMGR_ACCESS:
+      {
+	struct patmgr_info *inf;
+	int             dev, err;
+
+	inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+	IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+	dev = inf->device;
+
+	if (dev < 0 || dev >= num_synths)
+	  {
+	    KERNEL_FREE (inf);
+	    return RET_ERROR (ENXIO);
+	  }
+
+	if (!pmgr_present[dev])
+	  {
+	    KERNEL_FREE (inf);
+	    return RET_ERROR (ESRCH);
+	  }
+
+	if ((err = pmgr_access (dev, inf)) < 0)
+	  {
+	    KERNEL_FREE (inf);
+	    return err;
+	  }
+
+	IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+	KERNEL_FREE (inf);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_SEQ_TRESHOLD:
+      {
+	int             tmp = IOCTL_IN (arg);
+
+	if (dev)		/* Patch manager */
+	  return RET_ERROR (EIO);
+
+	if (tmp < 1)
+	  tmp = 1;
+	if (tmp >= SEQ_MAX_QUEUE)
+	  tmp = SEQ_MAX_QUEUE - 1;
+	output_treshold = tmp;
+	return 0;
+      }
+      break;
+
+    default:
+      if (dev)			/* Patch manager */
+	return RET_ERROR (EIO);
+
+      if (mode == OPEN_READ)
+	return RET_ERROR (EIO);
+
+      if (!synth_devs[0])
+	return RET_ERROR (ENXIO);
+      if (!(synth_open_mask & (1 << 0)))
+	return RET_ERROR (ENXIO);
+      return synth_devs[0]->ioctl (0, cmd, arg);
+      break;
+    }
+
+  return RET_ERROR (EINVAL);
+}
+
+#ifdef ALLOW_SELECT
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+  dev = dev >> 4;
+
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!iqlen)
+	{
+	  select_wait (&midi_sleeper, wait);
+	  return 0;
+	}
+      return 1;
+
+      break;
+
+    case SEL_OUT:
+      if (qlen >= SEQ_MAX_QUEUE)
+	{
+	  select_wait (&seq_sleeper, wait);
+	  return 0;
+	}
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
+}
+
+#endif
+
+void
+sequencer_timer (void)
+{
+  seq_startplay ();
+}
+
+int
+note_to_freq (int note_num)
+{
+
+  /*
+   * This routine converts a midi note to a frequency (multiplied by 1000)
+   */
+
+  int             note, octave, note_freq;
+  int             notes[] =
+  {
+    261632, 277189, 293671, 311132, 329632, 349232,
+    369998, 391998, 415306, 440000, 466162, 493880
+  };				/* Note freq*1000 for octave 5 */
+
+#define BASE_OCTAVE	5
+
+  octave = note_num / 12;
+  note = note_num % 12;
+
+  note_freq = notes[note];
+
+  if (octave < BASE_OCTAVE)
+    note_freq >>= (BASE_OCTAVE - octave);
+  else if (octave > BASE_OCTAVE)
+    note_freq <<= (octave - BASE_OCTAVE);
+
+  /* note_freq >>= 1;	 */
+
+  return note_freq;
+}
+
+unsigned long
+compute_finetune (unsigned long base_freq, int bend, int range)
+{
+  unsigned long   amount;
+  int             negative, semitones, cents;
+
+  if (!bend)
+    return base_freq;
+  if (!range)
+    return base_freq;
+
+  if (!base_freq)
+    return base_freq;
+
+  if (range >= 8192)
+    range = 8191;
+
+  bend = bend * range / 8192;
+  if (!bend)
+    return base_freq;
+
+  negative = bend < 0 ? 1 : 0;
+
+  if (bend < 0)
+    bend *= -1;
+  if (bend > range)
+    bend = range;
+
+  if (bend > 2399)
+    bend = 2399;
+
+  semitones = bend / 100;
+  cents = bend % 100;
+
+  amount = semitone_tuning[semitones] * cent_tuning[cents] / 10000;
+
+  if (negative)
+    return (base_freq * 10000) / amount;	/* Bend down */
+  else
+    return (base_freq * amount) / 10000;	/* Bend up */
+}
+
+
+long
+sequencer_init (long mem_start)
+{
+
+  sequencer_ok = 1;
+  return mem_start;
+}
+
+#else
+/* Stub version */
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+  {
+    return RET_ERROR (ENXIO);
+  }
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+  {
+  }
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+		 unsigned int cmd, unsigned int arg)
+{
+  return RET_ERROR (EIO);
+}
+
+int
+sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+  return RET_ERROR (EIO);
+}
+
+long
+sequencer_init (long mem_start)
+{
+  return mem_start;
+}
+
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+  return RET_ERROR (EIO);
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h
new file mode 100644
index 0000000..89bd796
--- /dev/null
+++ b/drivers/sound/sound_calls.h
@@ -0,0 +1,181 @@
+/*
+ *	DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_read (int dev, snd_rw_buf *user_buf, int count);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local);
+long DMAbuf_init(long mem_start);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+int DMAbuf_open_dma (int chan);
+void DMAbuf_close_dma (int chan);
+void DMAbuf_reset_dma (int chan);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev);
+
+/*
+ *	System calls for the /dev/dsp
+ */
+
+int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int dsp_open (int dev, struct fileinfo *file, int bits);
+void dsp_release (int dev, struct fileinfo *file);
+int dsp_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg);
+int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long dsp_init (long mem_start);
+
+/*
+ *	System calls for the /dev/audio
+ */
+
+int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_open (int dev, struct fileinfo *file);
+void audio_release (int dev, struct fileinfo *file);
+int audio_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg);
+int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long audio_init (long mem_start);
+
+/*
+ *	System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_open (int dev, struct fileinfo *file);
+void sequencer_release (int dev, struct fileinfo *file);
+int sequencer_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg);
+int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long sequencer_init (long mem_start);
+void sequencer_timer(void);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+
+#ifdef ALLOW_SELECT
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ *	System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_open (int dev, struct fileinfo *file);
+void MIDIbuf_release (int dev, struct fileinfo *file);
+int MIDIbuf_ioctl (int dev, struct fileinfo *file,
+	   unsigned int cmd, unsigned int arg);
+int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+long MIDIbuf_init(long mem_start);
+
+/*
+ *	System calls for the generic midi interface.
+ *
+ */
+
+long  CMIDI_init  (long mem_start);
+int   CMIDI_open  (int dev, struct fileinfo *file);
+int   CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int   CMIDI_read  (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int   CMIDI_close (int dev, struct fileinfo *file); 
+
+/*
+ *
+ *	Misc calls from various sources
+ */
+
+/* 	From pro_midi.c 	*/
+
+long pro_midi_attach(long mem_start);
+int  pro_midi_open(int dev, int mode);
+void pro_midi_close(int dev);
+int pro_midi_write(int dev, snd_rw_buf *uio);
+int pro_midi_read(int dev, snd_rw_buf *uio);
+
+/*	From soundcard.c	*/
+long soundcard_init(long mem_start);
+void tenmicrosec(void);
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+int snd_ioctl_return(int *addr, int value);
+
+/*	From sb_dsp.c	*/
+int sb_dsp_detect (struct address_info *hw_config);
+long sb_dsp_init (long mem_start, struct address_info *hw_config);
+void sb_dsp_disable_midi(void);
+
+/*	From opl3.c	*/
+int opl3_detect (int ioaddr);
+long opl3_init(long mem_start);
+
+/*	From sb_card.c	*/
+long attach_sb_card(long mem_start, struct address_info *hw_config);
+int probe_sb(struct address_info *hw_config);
+
+/*	From adlib_card.c	*/
+long attach_adlib_card(long mem_start, struct address_info *hw_config);
+int probe_adlib(struct address_info *hw_config);
+
+/*	From pas_card.c	*/
+long attach_pas_card(long mem_start, struct address_info *hw_config);
+int probe_pas(struct address_info *hw_config);
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/*	From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+long pas_pcm_init(long mem_start, struct address_info *hw_config);
+
+/*	From pas_mixer.c */
+int pas_init_mixer(void);
+
+/*	From pas_midi.c */
+long pas_midi_init(long mem_start);
+void pas_midi_interrupt(void);
+
+/*	From gus_card.c */
+long attach_gus_card(long mem_start, struct address_info * hw_config);
+int probe_gus(struct address_info *hw_config);
+int gus_set_midi_irq(int num);
+void gusintr(int);
+
+/*	From gus_wave.c */
+int gus_wave_detect(int baseaddr);
+long gus_wave_init(long mem_start, int irq, int dma);
+void gus_voice_irq(void);
+unsigned char gus_read8 (int reg);
+void gus_write8(int reg, unsigned char data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+
+/*	From gus_midi.c */
+long gus_midi_init(long mem_start);
+void gus_midi_interrupt(int dummy);
+
+/*	From mpu401.c */
+long attach_mpu401(long mem_start, struct address_info * hw_config);
+int probe_mpu401(struct address_info *hw_config);
+
+/*	From opl3.c */
+void enable_opl3_mode(int left, int right, int both);
+
+/*	From patmgr.c */
+int pmgr_open(int dev);
+void pmgr_release(int dev);
+int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_access(int dev, struct patmgr_info *rec);
+int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2,
+				    unsigned long parm3, unsigned long parm4);
diff --git a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h
new file mode 100644
index 0000000..7774543
--- /dev/null
+++ b/drivers/sound/sound_config.h
@@ -0,0 +1,217 @@
+/* sound_config.h
+ *
+ * A driver for Soundcards, misc configuration parameters.
+ *
+ * 
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "local.h"
+
+
+#undef CONFIGURE_SOUNDCARD
+#undef DYNAMIC_BUFFER
+
+#ifdef KERNEL_SOUNDCARD
+#define CONFIGURE_SOUNDCARD
+#define DYNAMIC_BUFFER
+#undef LOADABLE_SOUNDCARD
+#endif
+
+#ifdef EXCLUDE_SEQUENCER
+#define EXCLUDE_MIDI
+#define EXCLUDE_YM3812
+#define EXCLUDE_OPL3
+#endif
+
+/** UWM - new MIDI stuff **/
+
+#ifdef EXCLUDE_CHIP_MIDI
+#define EXCLUDE_PRO_MIDI
+#endif
+
+/** UWM - stuff **/
+ 
+#if defined(EXCLUDE_SEQUENCER) && defined(EXCLUDE_AUDIO)
+#undef CONFIGURE_SOUNDCARD
+#endif
+
+#ifdef CONFIGURE_SOUNDCARD
+
+/* ****** IO-address, DMA and IRQ settings ****
+
+If your card has nonstandard I/O address or IRQ number, change defines
+   for the following settings in your kernel Makefile */
+
+#ifndef SBC_BASE
+#define SBC_BASE	0x220	/* 0x220 is the factory default. */
+#endif
+
+#ifndef SBC_IRQ
+#define SBC_IRQ		7	/* IQR7 is the factory default.	 */
+#endif
+
+#ifndef SBC_DMA
+#define SBC_DMA		1
+#endif
+
+#ifndef PAS_BASE
+#define PAS_BASE	0x388
+#endif
+
+#ifndef PAS_IRQ
+#define PAS_IRQ		5
+#endif
+
+#ifndef PAS_DMA
+#define PAS_DMA		3
+#endif
+
+#ifndef GUS_BASE
+#define GUS_BASE	0x220
+#endif
+
+#ifndef GUS_IRQ
+#define GUS_IRQ		15
+#endif
+
+#ifndef GUS_MIDI_IRQ
+#define GUS_MIDI_IRQ	GUS_IRQ
+#endif
+
+#ifndef GUS_DMA
+#define GUS_DMA		6
+#endif
+
+#ifndef MPU_BASE
+#define MPU_BASE	0x330
+#endif
+
+#ifndef MPU_IRQ
+#define MPU_IRQ		6
+#endif
+
+/************* PCM DMA buffer sizes *******************/
+
+/* If you are using high playback or recording speeds, the default buffersize
+   is too small. DSP_BUFFSIZE must be 64k or less.
+
+   A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and
+   4k for SB.
+
+   If you change the DSP_BUFFSIZE, don't modify this file.
+   Use the make config command instead. */
+
+#ifndef DSP_BUFFSIZE
+#define DSP_BUFFSIZE		(4096)
+#endif
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT		2	/* 2 is recommended. */
+#endif
+
+#define DMA_AUTOINIT		0x10
+
+#define FM_MONO		0x388	/* This is the I/O address used by AdLib */
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+   driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE	1024
+
+#define SBFM_MAXINSTR		(256)	/* Size of the FM Instrument
+						   bank				 */
+/* 128 instruments for general MIDI setup and 16 unassigned	 */
+
+#define SND_NDEVS	50	/* Number of supported devices */
+#define SND_DEV_CTL	0	/* Control port /dev/mixer */
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MIDIN	2	/* MIDI input /dev/midin (not implemented
+				   yet) */
+#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS	6	/* /dev/sndstatus */
+
+/* UWM ... note add new MIDI devices here..  
+ *  Also do not forget to add table midi_supported[]
+ *  Minor numbers for on-chip midi devices start from 15.. and 
+ *  should be contiguous.. viz. 15,16,17....
+ * ERROR!!!!!!!!! NO NO. Minor numbers above 15 are reserved!!!!!! Hannu
+ *  Also note the max # of midi devices as MAX_MIDI_DEV
+ */ 
+
+#define CMIDI_DEV_PRO  15  	/* Chip midi device == /dev/pro_midi */
+
+/*
+ *  Add other midis here...
+		.
+		.
+		.
+		.
+ */
+
+#define DSP_DEFAULT_SPEED	8000
+
+#define ON		1
+#define OFF		0
+
+#define MAX_DSP_DEV	3
+#define MAX_MIXER_DEV	1
+#define MAX_SYNTH_DEV	3
+#define MAX_MIDI_DEV	3
+
+struct fileinfo {
+       	  int mode;	/* Open mode */
+       };
+
+struct address_info {
+	int io_base;
+	int irq;
+	int dma;
+};
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE		0x00
+#define WK_WAKEUP	0x01
+#define WK_TIMEOUT	0x02
+#define WK_SIGNAL	0x04
+#define WK_SLEEP	0x08
+
+#define OPEN_READ	1
+#define OPEN_WRITE	2
+#define OPEN_READWRITE	3
+
+#include "os.h"
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+#endif
diff --git a/drivers/sound/sound_stub.c b/drivers/sound/sound_stub.c
deleted file mode 100644
index e7f6255..0000000
--- a/drivers/sound/sound_stub.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-
-This file contains just a stub version of the Sound Driver.
-
-The real version is available at tsx-11.mit.edu
-(pub/linux/ALPHA/sound/snd-driv-0.x.tar.Z)
-
-Hannu Savolainen
-hsavolai@cs.helsinki.fi
-*/
-
-long soundcard_init(long mem_start)
-{
-	return mem_start;
-}
-
-void sound_mem_init(void)
-{
-}
-
-#ifdef CONFIG_SOUND
-#error The Sound Driver not installed.
-#endif
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
new file mode 100644
index 0000000..704e669
--- /dev/null
+++ b/drivers/sound/soundcard.c
@@ -0,0 +1,657 @@
+/*
+ * linux/kernel/chr_drv/sound/soundcard.c
+ * 
+ * Soundcard driver for Linux
+ * 
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include <linux/major.h>
+
+struct sbc_device
+{
+  int             usecount;
+};
+
+static struct sbc_device sbc_devices[SND_NDEVS];
+extern long     seq_time;
+
+static int      in_use = 0;	/* Total # of open device files (excluding
+				 * minor 0) */
+
+static int      soundcards_installed = 0;	/* Number of installed
+						 * soundcards */
+static int      soundcard_configured = 0;
+
+static struct fileinfo files[SND_NDEVS];
+
+extern char    *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int      snd_raw_count[MAX_DSP_DEV];
+
+/*
+ * /dev/sndstatus -device
+ */
+static char    *status_buf = NULL;
+static int      status_len, status_ptr;
+static int      status_busy = 0;
+
+static int
+put_status (char *s)
+{
+  int             l = strlen (s);
+
+  if (status_len + l >= 4000)
+    return 0;
+
+  memcpy (&status_buf[status_len], s, l);
+  status_len += l;
+
+  return 1;
+}
+
+static void
+init_status (void)
+{
+  /*
+   * Write the status information to the status_buf and update status_len.
+   * There is a limit of 4000 bytes for the data.
+   */
+
+  char            tmp_buf[256];	/* Line buffer */
+  int             i;
+
+  status_ptr = 0;
+
+  put_status ("Sound Driver:" SOUND_VERSION_STRING
+	      " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+	      SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+	      "\n");
+
+  sprintf (tmp_buf, "Config options: 0x%08x\n\n", SELECTED_SOUND_OPTIONS);
+  if (!put_status (tmp_buf))
+    return;
+
+  sprintf (tmp_buf, "Major number: %d\n", SOUND_MAJOR);
+  if (!put_status (tmp_buf))
+    return;
+
+  if (!put_status ("HW config: \n"))
+    return;
+
+  for (i = 0; i < (num_sound_drivers - 1); i++)
+    {
+      sprintf (tmp_buf, "Type %d: %s ",
+	       supported_drivers[i].card_type,
+	       supported_drivers[i].name);
+      if (!put_status (tmp_buf))
+	return;
+
+      sprintf (tmp_buf, " at 0x%03x irq %d drq %d\n",
+	       supported_drivers[i].config.io_base,
+	       supported_drivers[i].config.irq,
+	       supported_drivers[i].config.dma);
+      if (!put_status (tmp_buf))
+	return;
+    }
+
+  if (!put_status ("\nPCM devices:\n"))
+    return;
+
+  for (i = 0; i < num_dspdevs; i++)
+    {
+      sprintf (tmp_buf, "%02d: %s\n", i, dsp_devs[i]->name);
+      if (!put_status (tmp_buf))
+	return;
+    }
+
+  if (!put_status ("\nSynth devices:\n"))
+    return;
+
+  for (i = 0; i < num_synths; i++)
+    {
+      sprintf (tmp_buf, "%02d: %s\n", i, synth_devs[i]->info->name);
+      if (!put_status (tmp_buf))
+	return;
+    }
+
+  if (!put_status ("\nMidi devices:\n"))
+    return;
+
+  for (i = 0; i < num_midis; i++)
+    {
+      sprintf (tmp_buf, "%02d: %s\n", i, midi_devs[i]->info.name);
+      if (!put_status (tmp_buf))
+	return;
+    }
+
+  if (num_mixers)
+    {
+      if (!put_status ("\nMixer(s) installed\n"))
+	return;
+    }
+  else
+    {
+      if (!put_status ("\nNo mixers installed\n"))
+	return;
+    }
+}
+
+static int
+read_status (char *buf, int count)
+{
+  /*
+   * Return at most 'count' bytes from the status_buf.
+   */
+  int             l, c;
+
+  l = count;
+  c = status_len - status_ptr;
+
+  if (l > c)
+    l = c;
+  if (l <= 0)
+    return 0;
+
+  memcpy_tofs (buf, &status_buf[status_ptr], l);
+  status_ptr += l;
+
+  return l;
+}
+
+int
+snd_ioctl_return (int *addr, int value)
+{
+  if (value < 0)
+    return value;
+
+  PUT_WORD_TO_USER (addr, 0, value);
+  return 0;
+}
+
+static int
+sound_read (struct inode *inode, struct file *file, char *buf, int count)
+{
+  int             dev;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));
+
+  switch (dev & 0x0f)
+    {
+    case SND_DEV_STATUS:
+      return read_status (buf, count);
+      break;
+
+    case SND_DEV_AUDIO:
+      return audio_read (dev, &files[dev], buf, count);
+      break;
+
+    case SND_DEV_DSP:
+    case SND_DEV_DSP16:
+      return dsp_read (dev, &files[dev], buf, count);
+      break;
+
+    case SND_DEV_SEQ:
+      return sequencer_read (dev, &files[dev], buf, count);
+      break;
+
+#ifndef EXCLUDE_MPU401
+    case SND_DEV_MIDIN:
+      return MIDIbuf_read (dev, &files[dev], buf, count);
+#endif
+
+    default:
+      printk ("Sound: Undefined minor device %d\n", dev);
+    }
+
+  return RET_ERROR (EPERM);
+}
+
+static int
+sound_write (struct inode *inode, struct file *file, char *buf, int count)
+{
+  int             dev;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));
+
+  switch (dev & 0x0f)
+    {
+
+    case SND_DEV_SEQ:
+      return sequencer_write (dev, &files[dev], buf, count);
+      break;
+
+    case SND_DEV_AUDIO:
+      return audio_write (dev, &files[dev], buf, count);
+      break;
+
+    case SND_DEV_DSP:
+    case SND_DEV_DSP16:
+      return dsp_write (dev, &files[dev], buf, count);
+      break;
+
+    default:
+      return RET_ERROR (EPERM);
+    }
+
+  return count;
+}
+
+static int
+sound_lseek (struct inode *inode, struct file *file, off_t offset, int orig)
+{
+  return RET_ERROR (EPERM);
+}
+
+static int
+sound_open (struct inode *inode, struct file *file)
+{
+  int             dev, retval;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+  if ((dev >= SND_NDEVS) || (dev < 0))
+    {
+      printk ("Invalid minor device %d\n", dev);
+      return RET_ERROR (ENODEV);
+    }
+
+  if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
+    {
+      printk ("SoundCard Error: The soundcard system has not been configured\n");
+      return RET_ERROR (ENODEV);
+    }
+
+  files[dev].mode = 0;
+
+  if ((file->f_flags & O_ACCMODE) == O_RDWR)
+    files[dev].mode = OPEN_READWRITE;
+  if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+    files[dev].mode = OPEN_READ;
+  if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+    files[dev].mode = OPEN_WRITE;
+
+  switch (dev & 0x0f)
+    {
+    case SND_DEV_STATUS:
+      if (status_busy)
+	return RET_ERROR (EBUSY);
+      status_busy = 1;
+      if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
+	return RET_ERROR (EIO);
+      status_len = status_ptr = 0;
+      init_status ();
+      break;
+
+    case SND_DEV_CTL:
+      if (!soundcards_installed)
+	if (soundcard_configured)
+	  {
+	    printk ("Soundcard not installed\n");
+	    return RET_ERROR (ENODEV);
+	  }
+      break;
+
+    case SND_DEV_SEQ:
+      if ((retval = sequencer_open (dev, &files[dev])) < 0)
+	return retval;
+      break;
+
+#ifndef EXCLUDE_MPU401
+    case SND_DEV_MIDIN:
+      if ((retval = MIDIbuf_open (dev, &files[dev])) < 0)
+	return retval;
+      break;
+#endif
+
+    case SND_DEV_AUDIO:
+      if ((retval = audio_open (dev, &files[dev])) < 0)
+	return retval;
+      break;
+
+    case SND_DEV_DSP:
+      if ((retval = dsp_open (dev, &files[dev], 8)) < 0)
+	return retval;
+      break;
+
+    case SND_DEV_DSP16:
+      if ((retval = dsp_open (dev, &files[dev], 16)) < 0)
+	return retval;
+      break;
+
+    default:
+      printk ("Invalid minor device %d\n", dev);
+      return RET_ERROR (ENODEV);
+    }
+
+  sbc_devices[dev].usecount++;
+  in_use++;
+
+  return 0;
+}
+
+static void
+sound_release (struct inode *inode, struct file *file)
+{
+  int             dev;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_release(dev=%d)\n", dev));
+
+  switch (dev & 0x0f)
+    {
+    case SND_DEV_STATUS:
+      if (status_buf)
+	KERNEL_FREE (status_buf);
+      status_buf = NULL;
+      status_busy = 0;
+      break;
+
+    case SND_DEV_CTL:
+      break;
+
+    case SND_DEV_SEQ:
+      sequencer_release (dev, &files[dev]);
+      break;
+
+#ifndef EXCLUDE_MPU401
+    case SND_DEV_MIDIN:
+      MIDIbuf_release (dev, &files[dev]);
+      break;
+#endif
+
+    case SND_DEV_AUDIO:
+      audio_release (dev, &files[dev]);
+      break;
+
+    case SND_DEV_DSP:
+    case SND_DEV_DSP16:
+      dsp_release (dev, &files[dev]);
+      break;
+
+    default:
+      printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
+    }
+
+  sbc_devices[dev].usecount--;
+  in_use--;
+}
+
+static int
+sound_ioctl (struct inode *inode, struct file *file,
+	     unsigned int cmd, unsigned long arg)
+{
+  int             dev;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+  switch (dev & 0x0f)
+    {
+
+    case SND_DEV_CTL:
+
+      if (!num_mixers)
+	return RET_ERROR (ENODEV);
+
+      if (dev >= num_mixers)
+	return RET_ERROR (ENODEV);
+
+      return mixer_devs[dev]->ioctl (dev, cmd, arg);
+      break;
+
+    case SND_DEV_SEQ:
+      return sequencer_ioctl (dev, &files[dev], cmd, arg);
+      break;
+
+    case SND_DEV_AUDIO:
+      return audio_ioctl (dev, &files[dev], cmd, arg);
+      break;
+
+    case SND_DEV_DSP:
+    case SND_DEV_DSP16:
+      return dsp_ioctl (dev, &files[dev], cmd, arg);
+      break;
+
+#ifndef EXCLUDE_MPU401
+    case SND_DEV_MIDIN:
+      return MIDIbuf_ioctl (dev, &files[dev], cmd, arg);
+      break;
+#endif
+
+    default:
+      return RET_ERROR (EPERM);
+      break;
+    }
+
+  return RET_ERROR (EPERM);
+}
+
+static int
+sound_select (struct inode *inode, struct file *file, int sel_type, select_table * wait)
+{
+  int             dev;
+
+  dev = inode->i_rdev;
+  dev = MINOR (dev);
+
+  DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
+
+  switch (dev & 0x0f)
+    {
+    case SND_DEV_SEQ:
+      return sequencer_select (dev, &files[dev], sel_type, wait);
+      break;
+
+    default:
+      return 0;
+    }
+
+  return 0;
+}
+
+static struct file_operations sound_fops =
+{
+  sound_lseek,
+  sound_read,
+  sound_write,
+  NULL,				/* sound_readdir */
+  sound_select,
+  sound_ioctl,
+  NULL,
+  sound_open,
+  sound_release
+};
+
+long
+soundcard_init (long mem_start)
+{
+  int             i;
+
+  register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+
+  soundcard_configured = 1;
+
+  mem_start = sndtable_init (mem_start);	/* Initialize call tables and
+						 * detect cards */
+
+  if (!(soundcards_installed = sndtable_get_cardcount ()))
+    return mem_start;		/* No cards detected */
+
+  if (num_dspdevs)		/* Audio devices present */
+    {
+      mem_start = DMAbuf_init (mem_start);
+      mem_start = audio_init (mem_start);
+      mem_start = dsp_init (mem_start);
+    }
+
+#ifndef EXCLUDE_MPU401
+  if (num_midis)
+    mem_start = MIDIbuf_init (mem_start);
+#endif
+
+  if (num_midis + num_synths)
+    mem_start = sequencer_init (mem_start);
+
+  for (i = 0; i < SND_NDEVS; i++)
+    {
+      sbc_devices[i].usecount = 0;
+    }
+
+  return mem_start;
+}
+
+void
+tenmicrosec (void)
+{
+  int             i;
+
+  for (i = 0; i < 16; i++)
+    inb (0x80);
+}
+
+void
+request_sound_timer (int count)
+{
+  if (count < 0)
+    count = jiffies + (-count);
+  else
+    count += seq_time;
+  timer_table[SOUND_TIMER].fn = sequencer_timer;
+  timer_table[SOUND_TIMER].expires = count;
+  timer_active |= 1 << SOUND_TIMER;
+}
+
+void
+sound_stop_timer (void)
+{
+  timer_table[SOUND_TIMER].expires = 0;
+  timer_active &= ~(1 << SOUND_TIMER);
+}
+
+#ifndef EXCLUDE_AUDIO
+static int
+valid_dma_page (unsigned long addr, unsigned long dev_buffsize, unsigned long dma_pagesize)
+{
+  if (((addr & (dma_pagesize - 1)) + dev_buffsize) <= dma_pagesize)
+    return 1;
+  else
+    return 0;
+}
+
+void
+sound_mem_init (void)
+{
+  int             i, dev;
+  unsigned long   start_addr, end_addr, mem_ptr, dma_pagesize;
+
+  mem_ptr = high_memory;
+
+  /* Some sanity checks */
+
+  if (mem_ptr > (16 * 1024 * 1024))
+    mem_ptr = 16 * 1024 * 1024;	/* Limit to 16M */
+
+  for (dev = 0; dev < num_dspdevs; dev++)	/* Enumerate devices */
+    if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
+      {
+	if (sound_dma_automode[dev])
+	  sound_buffcounts[dev] = 1;
+
+	if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
+	  dma_pagesize = 131072;/* 128k */
+	else
+	  dma_pagesize = 65536;
+
+	/* More sanity checks */
+
+	if (sound_buffsizes[dev] > dma_pagesize)
+	  sound_buffsizes[dev] = dma_pagesize;
+	sound_buffsizes[dev] &= 0xfffff000;	/* Truncate to n*4k */
+	if (sound_buffsizes[dev] < 4096)
+	  sound_buffsizes[dev] = 4096;
+
+	/* Now allocate the buffers */
+
+	for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
+	  {
+	    start_addr = mem_ptr - sound_buffsizes[dev];
+	    if (!valid_dma_page (start_addr, sound_buffsizes[dev], dma_pagesize))
+	      start_addr &= ~(dma_pagesize - 1);	/* Align address to
+							 * dma_pagesize */
+
+	    end_addr = start_addr + sound_buffsizes[dev] - 1;
+
+	    snd_raw_buf[dev][snd_raw_count[dev]] = (char *) start_addr;
+	    snd_raw_buf_phys[dev][snd_raw_count[dev]] = start_addr;
+	    mem_ptr = start_addr;
+
+	    for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++)
+	      {
+		if (mem_map[i])
+		  panic ("sound_mem_init: Page not free (driver incompatible with kernel).\n");
+
+		mem_map[i] = MAP_PAGE_RESERVED;
+	      }
+	  }
+      }				/* for dev */
+}
+
+#endif
+
+#else
+
+long
+soundcard_init (long mem_start)	/* Dummy version */
+{
+  return mem_start;
+}
+
+#endif
+
+#if !defined(CONFIGURE_SOUNDCARD) || defined(EXCLUDE_AUDIO)
+void
+sound_mem_init (void)
+{
+  /* Dummy version */
+}
+
+#endif
diff --git a/drivers/sound/soundcard.h b/drivers/sound/soundcard.h
new file mode 100644
index 0000000..2c089ed
--- /dev/null
+++ b/drivers/sound/soundcard.h
@@ -0,0 +1,1474 @@
+#ifndef SOUNDCARD_H
+#define SOUNDCARD_H
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * 
+ */
+
+ /* 
+  * If you make modifications to this file, please contact me before
+  * distributing the modified version. There is already enough 
+  * divercity in the world.
+  *
+  * Regards,
+  * Hannu Savolainen
+  * hsavolai@cs.helsinki.fi
+  */
+
+#define SOUND_VERSION	200
+
+#include <sys/ioctl.h>
+
+/*
+ *	Supported card ID numbers (Should be somewhere else?)
+ */
+
+#define SNDCARD_ADLIB	1
+#define SNDCARD_SB	2
+#define SNDCARD_PAS	3
+#define SNDCARD_GUS	4
+#define SNDCARD_MPU401	5
+
+/***********************************
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#ifndef _IOWR
+/*	@(#)ioctlp.h */
+
+/* Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word.  The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+/* #define	IOCTYPE		(0xff<<8) */
+#define	IOCPARM_MASK	0x7f		/* parameters must be < 128 bytes */
+#define	IOC_VOID	0x20000000	/* no parameters */
+#define	IOC_OUT		0x40000000	/* copy out parameters */
+#define	IOC_IN		0x80000000	/* copy in parameters */
+#define	IOC_INOUT	(IOC_IN|IOC_OUT)
+/* the 0x20000000 is so we can distinguish new ioctl's from old */
+#define	_IO(x,y)	((int)(IOC_VOID|(x<<8)|y))
+#define	_IOR(x,y,t)	((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#define	_IOW(x,y,t)	((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+/* this should be _IORW, but stdio got there first */
+#define	_IOWR(x,y,t)	((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#endif  /* !_IOWR */
+
+#define SNDCTL_SEQ_RESET		_IO  ('Q', 0)
+#define SNDCTL_SEQ_SYNC			_IO  ('Q', 1)
+#define SNDCTL_SYNTH_INFO		_IOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE		_IOWR('Q', 3, int)	/* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT		_IOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT		_IOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE		_IOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR		_IOW ('Q', 7, struct sbi_instrument)	/* Valid for FM only */
+#define SNDCTL_SEQ_TESTMIDI		_IOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES		_IOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS		_IOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS		_IOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO		_IOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_TRESHOLD		_IOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL		_IOWR('Q',14, int)	/* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE		_IOW ('Q',15, int)	/* in=dev# */
+#define SNDCTL_PMGR_ACCESS		_IOWR('Q',16, struct patmgr_info)
+
+/*
+ *	Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ *	The following patch_info structure has been designed to support
+ *	Gravis UltraSound. It tries to be universal format for uploading
+ *	sample based patches but is propably too limited.
+ */
+
+struct patch_info {
+		short key;		/* Use GUS_PATCH here */
+#define GUS_PATCH	0x04fd
+#define OBSOLETE_GUS_PATCH	0x02fd
+		short device_no;	/* Synthesizer number */
+		short instr_no;		/* Midi pgm# */
+
+		unsigned long mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS	0x01	/* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED	0x02	/* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING	0x04	/* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP	0x08	/* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK	0x10	/* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON	0x20	/* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES	0x40	/* bit 6 = Enable envelopes - 1 */
+				/* 	(use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO	0x00010000	/* The vibrato info is valid */
+#define WAVE_TREMOLO	0x00020000	/* The tremolo info is valid */
+#define WAVE_SCALE	0x00040000	/* The scaling info is valid */
+/* Other bits must be zeroed */
+
+		long len;	/* Size of the wave data in bytes */
+		long loop_start, loop_end; /* Byte offsets from the beginning */
+
+/* 
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for a instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+		unsigned int base_freq;
+		unsigned long base_note;
+		unsigned long high_note;
+		unsigned long low_note;
+		int panning;	/* -128=left, 127=right */
+		int detuning;
+
+/*	New fields introduced in version 1.99.5	*/
+
+       /* Envelope. Enabled by mode bit WAVE_ENVELOPES	*/
+		unsigned char	env_rate[ 6 ];	 /* GUS HW ramping rate */
+		unsigned char	env_offset[ 6 ]; /* 255 == 100% */
+
+	/* 
+	 * The tremolo, vibrato and scale info are not supported yet.
+	 * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+	 * WAVE_SCALE
+	 */
+
+		unsigned char	tremolo_sweep;
+		unsigned char	tremolo_rate;
+		unsigned char	tremolo_depth;
+	
+		unsigned char	vibrato_sweep;
+		unsigned char	vibrato_rate;
+		unsigned char	vibrato_depth;
+
+		int		scale_frequency;
+		unsigned int	scale_factor;		/* from 0 to 2048 or 0 to 2 */
+	
+	        int		volume;
+	        int		spare[4];
+		char data[1];	/* The waveform data starts here */
+	};
+
+
+/*
+ * Patch management interface (/dev/sequencer, /dev/patmgr#)
+ * Don't use these calls if you want to maintain compatibility with
+ * the future versions of the driver.
+ */
+
+#define 	PS_NO_PATCHES		0	/* No patch support on device */
+#define		PS_MGR_NOT_OK		1	/* Plain patch support (no mgr) */
+#define		PS_MGR_OK		2	/* Patch manager supported */
+#define		PS_MANAGED		3	/* Patch manager running */
+
+#define SNDCTL_PMGR_IFACE		_IOWR('P', 1, struct patmgr_info)
+
+/*
+ * The patmgr_info is a fixed size structure which is used for two
+ * different purposes. The intended use is for communication between
+ * the application using /dev/sequencer and the patch manager daemon
+ * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
+ *
+ * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
+ * a patch manager daemon to read and write device parameters. This
+ * ioctl available through /dev/sequencer also. Avoid using it since it's
+ * extremely hardware dependent. In addition access trough /dev/sequencer 
+ * may confuse the patch manager daemon.
+ */
+
+struct patmgr_info {	/* Note! size must be < 4k since kmalloc() is used */
+	  unsigned long key;	/* Don't worry. Reserved for communication
+	  			   between the patch manager and the driver. */
+#define PM_K_EVENT		1 /* Event from the /dev/sequencer driver */
+#define PM_K_COMMAND		2 /* Request from a application */
+#define PM_K_RESPONSE		3 /* From patmgr to application */
+#define PM_ERROR		4 /* Error returned by the patmgr */
+	  int device;
+	  int command;
+
+/* 
+ * Commands 0x000 to 0xfff reserved for patch manager programs 
+ */
+#define PM_GET_DEVTYPE	1	/* Returns type of the patch mgr interface of dev */
+#define		PMTYPE_FM2	1	/* 2 OP fm */
+#define		PMTYPE_FM4	2	/* Mixed 4 or 2 op FM (OPL-3) */
+#define		PMTYPE_WAVE	3	/* Wave table synthesizer (GUS) */
+#define PM_GET_NRPGM	2	/* Returns max # of midi programs in parm1 */
+#define PM_GET_PGMMAP	3	/* Returns map of loaded midi programs in data8 */
+#define PM_GET_PGM_PATCHES 4	/* Return list of patches of a program (parm1) */
+#define PM_GET_PATCH	5	/* Return patch header of patch parm1 */
+#define PM_SET_PATCH	6	/* Set patch header of patch parm1 */
+#define PM_READ_PATCH	7	/* Read patch (wave) data */
+#define PM_WRITE_PATCH	8	/* Write patch (wave) data */
+
+/*
+ * Commands 0x1000 to 0xffff are for communication between the patch manager
+ * and the client
+ */
+#define _PM_LOAD_PATCH	0x100
+
+/* 
+ * Commands above 0xffff reserved for device specific use
+ */
+
+	  long parm1;
+	  long parm2;
+	  long parm3;
+
+	  union {
+		unsigned char data8[4000];
+		unsigned short data16[2000];
+		unsigned long data32[1000];
+		struct patch_info patch;
+	  } data;
+	};
+
+/*
+ * When a patch manager daemon is present, it will be informed by the
+ * driver when something important happens. For example when the
+ * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
+ * returned. The command field contains the event type:
+ */
+#define PM_E_OPENED		1	/* /dev/sequencer opened */
+#define PM_E_CLOSED		2	/* /dev/sequencer closed */
+#define PM_E_PATCH_RESET	3	/* SNDCTL_RESETSAMPLES called */
+#define PM_E_PATCH_LOADED	4	/* A patch has been loaded by appl */
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size. 
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 0xf0,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF		0
+#define SEQ_FMNOTEOFF		SEQ_NOTEOFF	/* Just old name */
+#define SEQ_NOTEON		1
+#define	SEQ_FMNOTEON		SEQ_NOTEON
+#define SEQ_WAIT		2
+#define SEQ_PGMCHANGE		3
+#define SEQ_FMPGMCHANGE		SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER		4
+#define SEQ_MIDIPUTC		5
+#define SEQ_DRUMON		6	/*** OBSOLETE ***/
+#define SEQ_DRUMOFF		7	/*** OBSOLETE ***/
+#define SEQ_ECHO		8	/* For synching programs with output */
+#define SEQ_AFTERTOUCH		9
+#define SEQ_CONTROLLER		10
+#define    CTRL_PITCH_BENDER		255
+#define    CTRL_PITCH_BENDER_RANGE	254
+#define    CTRL_EXPRESSION		253
+#define    CTRL_MAIN_VOLUME		252
+#define SEQ_BALANCE		11
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ *	 input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE		0xfd	/* Long events */
+/*
+ *	SEQ_FULLSIZE events are used for loading patches/samples to the
+ *	synthesizer devices. These events are passed directly to the driver
+ *	of the associated synthesizer device. There is no limit to the size
+ *	of the extended events. These events are not queued but executed
+ *	immediately when the write() is called (execution can take several
+ *	seconds of time). 
+ *
+ *	When a SEQ_FULLSIZE message is written to the device, it must
+ *	be written using exactly one write() call. Other events cannot
+ *	be mixed to the same write.
+ *	
+ *	For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the 
+ *	/dev/sequencer. Don't write other data together with the instrument structure
+ *	Set the key field of the structure to FM_PATCH. The device field is used to
+ *	route the patch to the corresponding device.
+ *
+ *	For Gravis UltraSound use struct patch_info. Initialize the key field
+ *      to GUS_PATCH.
+ */
+#define SEQ_PRIVATE		0xfe	/* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED		0xff	/* Extended events (8 bytes) */
+
+/*
+ *	Extended events for synthesizers (8 bytes)
+ *
+ *	Format:
+ *
+ *		b0	= SEQ_EXTENDED
+ *		b1	= command
+ *		b2	= device
+ *		b3-b7	= parameters
+ *
+ *	Command				b3	b4	b5	b6	b7
+ *	----------------------------------------------------------------------------
+ *	SEQ_NOTEON			voice	note	volume	0	0
+ *	SEQ_NOTEOFF			voice	note	volume	0	0
+ *	SEQ_PGMCHANGE			voice	pgm	0	0	0
+ *	SEQ_DRUMON			(voice)	drum#	volume	0	0
+ *	SEQ_DRUMOFF			(voice)	drum#	volume	0	0
+ */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument {
+		unsigned short	key;		/* 	Initialize to FM_PATCH or OPL3_PATCH */
+#define FM_PATCH	0x01fd
+#define OPL3_PATCH	0x03fd
+		short		device;		/*	Synth# (0-4)	*/
+		int 		channel;	/*	Program# to be initialized 	*/
+		sbi_instr_data	operators;	/*	Register settings for operator cells (.SBI format)	*/
+	};
+
+struct synth_info {	/* Read only */
+		char	name[30];
+		int	device;		/* 0-N. INITIALIZE BEFORE CALLING */
+		int	synth_type;
+#define SYNTH_TYPE_FM			0
+#define SYNTH_TYPE_SAMPLE		1
+
+		int	synth_subtype;
+#define FM_TYPE_ADLIB			0x00
+#define FM_TYPE_OPL3			0x01
+
+#define SAMPLE_TYPE_GUS			0x10
+
+		int	perc_mode;	/* No longer supported */
+		int	nr_voices;
+		int	nr_drums;	/* Obsolete field */
+		int	instr_bank_size;
+		unsigned long	capabilities;	
+#define SYNTH_CAP_PERCMODE		0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3			0x00000002 /* Set if OPL3 supported */
+		int	dummies[19];	/* Reserve space */
+	};
+
+struct midi_info {
+		char		name[30];
+		int		device;		/* 0-N. INITIALIZE BEFORE CALLING */
+		unsigned long	capabilities;	/* To be defined later */
+		int		dummies[19];	/* Reserve space */
+	};
+
+/********************************************
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET		_IO  ('P', 0)
+#define SNDCTL_DSP_SYNC			_IO  ('P', 1)
+#define SNDCTL_DSP_SPEED		_IOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO		_IOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE		_IOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE		_IOWR('P', 5, int)	/* 8, 12 or 16 */
+#define SOUND_PCM_WRITE_CHANNELS	_IOWR('P', 6, int)
+#define SOUND_PCM_WRITE_FILTER		_IOWR('P', 7, int)
+#define SNDCTL_DSP_POST			_IO  ('P', 8)
+
+#define SOUND_PCM_READ_RATE		_IOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS		_IOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS		_IOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER		_IOR ('P', 7, int)
+
+/* Some alias names */
+#define SOUND_PCM_WRITE_BITS		SNDCTL_DSP_SAMPLESIZE
+#define SOUND_PCM_WRITE_RATE		SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST			SNDCTL_DSP_POST
+#define SOUND_PCM_RESET			SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC			SNDCTL_DSP_SYNC
+
+/*********************************************
+ * IOCTL commands for /dev/mixer
+ */
+	
+/* 
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum. 
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES	12
+#define SOUND_MIXER_VOLUME	0
+#define SOUND_MIXER_BASS	1
+#define SOUND_MIXER_TREBLE	2
+#define SOUND_MIXER_SYNTH	3
+#define SOUND_MIXER_PCM		4
+#define SOUND_MIXER_SPEAKER	5
+#define SOUND_MIXER_LINE	6
+#define SOUND_MIXER_MIC		7
+#define SOUND_MIXER_CD		8
+#define SOUND_MIXER_IMIX	9	/*  Recording monitor  */
+#define SOUND_MIXER_ALTPCM	10
+#define SOUND_MIXER_RECLEV	11	/* Recording level */
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN		28
+#define SOUND_ONOFF_MAX		30
+#define SOUND_MIXER_MUTE	28	/* 0 or 1 */
+#define SOUND_MIXER_ENHANCE	29	/* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_LOUD	30	/* 0 or 1 */
+
+/* Note!	Number 31 cannot be used since the sign bit is reserved */
+
+#define SOUND_DEVICE_LABELS	{"Vol  ", "Bass ", "Trebl", "Synth", "Pcm  ", "Spkr ", "Line ", \
+				 "Mic  ", "CD   ", "Mix  ", "Pcm2 ", "rec"}
+
+#define SOUND_DEVICE_NAMES	{"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+				 "mic", "cd", "mix", "pcm2", "rec"}
+
+/*	Device bitmask identifiers	*/
+
+#define SOUND_MIXER_RECSRC	0xff	/* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK	0xfe	/* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK	0xfd	/* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS	0xfc
+	#define SOUND_CAP_EXCL_INPUT	0x00000001	/* Only one recording source at a time */
+#define SOUND_MIXER_STEREODEVS	0xfb	/* Mixer channels supporting stereo */
+
+/*	Device mask bits	*/
+
+#define SOUND_MASK_VOLUME	(1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS		(1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE	(1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH	(1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM		(1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER	(1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE		(1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC		(1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD		(1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX		(1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM	(1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV	(1 << SOUND_MIXER_RECLEV)
+
+#define SOUND_MASK_MUTE		(1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE	(1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD		(1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev)		_IOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME		MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS		MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE		MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH		MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM		MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER	MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE		MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC		MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD		MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX		MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM		MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV		MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_MUTE		MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE	MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD		MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC		MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK	MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK	MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS	MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS		MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev)		_IOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME	MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS		MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE	MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH		MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM		MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER	MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE		MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC		MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD		MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX		MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM	MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV	MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_MUTE		MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE	MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD		MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC	MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/*
+ *	The following mixer ioctl calls are compatible with the BSD driver by
+ *	  Steve Haehnichen <shaehnic@ucsd.edu>
+ *
+ * Since this interface is entirely SB specific, it will be dropped in the
+ * near future.
+ */
+
+typedef unsigned char S_BYTE;
+typedef unsigned char S_FLAG;
+struct stereo_vol
+{
+  S_BYTE l;			/* Left volume */
+  S_BYTE r;			/* Right volume */
+};
+
+#define MIXER_IOCTL_SET_LEVELS 		_IOW ('s', 20, struct sb_mixer_levels)
+#define MIXER_IOCTL_SET_PARAMS 		_IOW ('s', 21, struct sb_mixer_params)
+#define MIXER_IOCTL_READ_LEVELS 	_IOR ('s', 22, struct sb_mixer_levels)
+#define MIXER_IOCTL_READ_PARAMS 	_IOR ('s', 23, struct sb_mixer_params)
+#define MIXER_IOCTL_RESET		_IO  ('s', 24)
+
+/*
+ * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
+ */
+struct sb_mixer_levels
+{
+  struct stereo_vol master;	/* Master volume */
+  struct stereo_vol voc;	/* DSP Voice volume */
+  struct stereo_vol fm;		/* FM volume */
+  struct stereo_vol line;	/* Line-in volume */
+  struct stereo_vol cd;		/* CD audio */
+  S_BYTE mic;			/* Microphone level */
+};
+
+/*
+ * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
+ */
+struct sb_mixer_params
+{
+  S_BYTE record_source;		/* Recording source (See SRC_xxx below) */
+  S_FLAG hifreq_filter;		/* Filter frequency (hi/low) */
+  S_FLAG filter_input;		/* ANFI input filter */
+  S_FLAG filter_output;		/* DNFI output filter */
+  S_FLAG dsp_stereo;		/* 1 if DSP is in Stereo mode */
+};
+
+#define SRC_MIC         1	/* Select Microphone recording source */
+#define SRC_CD          3	/* Select CD recording source */
+#define SRC_LINE        7	/* Use Line-in for recording source */
+
+#if !defined(KERNEL) && !defined(INKERNEL)
+/*
+ *	Some convenience macros to simplify programming of the
+ *	/dev/sequencer interface
+ *
+ *	These macros define the API which should be used when possible.
+ */
+
+void seqbuf_dump(void);	/* This function must be provided by programs */
+
+/* Sample seqbuf_dump() implementation:
+ *
+ *	SEQ_DEFINEBUF (2048);	-- Defines a buffer for 2048 bytes
+ *
+ *	int seqfd;		-- The file descriptor for /dev/sequencer.
+ *
+ *	void
+ *	seqbuf_dump ()
+ *	{
+ *	  if (_seqbufptr)
+ *	    if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ *	      {
+ *		perror ("write /dev/sequencer");
+ *		exit (-1);
+ *	      }
+ *	  _seqbufptr = 0;
+ *	}
+ */
+
+#define SEQ_DEFINEBUF(len)		unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0
+#define SEQ_PM_DEFINES			struct patmgr_info _pm_info
+#define _SEQ_NEEDBUF(len)		if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len)		_seqbufptr += len
+#define SEQ_DUMPBUF			seqbuf_dump
+#define PM_LOAD_PATCH(dev, bank, pgm)	(SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+					_pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
+					_pm_info.parm1 = bank, _pm_info.parm2 = 1, \
+					ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+					_pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \
+					_pm_info.parm1 = bank, _pm_info.parm2 = 128, \
+					ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+
+#define SEQ_START_NOTE(dev, voice, note, vol)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_NOTEON;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (note);\
+					_seqbuf[_seqbufptr+5] = (vol);\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_STOP_NOTE(dev, voice, note, vol)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (note);\
+					_seqbuf[_seqbufptr+5] = (vol);\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, voice, pressure)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (pressure);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_PANNING(dev, voice, pos)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_BALANCE;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					(char)_seqbuf[_seqbufptr+4] = (pos);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_CONTROL(dev, voice, controller, value)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (controller);\
+					*(short *)&_seqbuf[_seqbufptr+5] = (value);\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value)
+#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value)
+
+#define SEQ_START_TIMER()		{_SEQ_NEEDBUF(4);\
+					_seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\
+					_seqbuf[_seqbufptr+1] = 0;\
+					_seqbuf[_seqbufptr+2] = 0;\
+					_seqbuf[_seqbufptr+3] = 0;\
+					_SEQ_ADVBUF(4);}
+#define SEQ_SET_PATCH(dev, voice, patch)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (patch);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_WAIT_TIME(ticks)		{_SEQ_NEEDBUF(4);\
+				 	*(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\
+				 	_SEQ_ADVBUF(4);}
+
+#define SEQ_ECHO_BACK(key)		{_SEQ_NEEDBUF(4);\
+				 	*(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\
+				 	_SEQ_ADVBUF(4);}
+
+#define SEQ_MIDIOUT(device, byte)	{_SEQ_NEEDBUF(4);\
+					_seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+					_seqbuf[_seqbufptr+1] = (byte);\
+					_seqbuf[_seqbufptr+2] = (device);\
+					_seqbuf[_seqbufptr+3] = 0;\
+					_SEQ_ADVBUF(4);}
+#define SEQ_WRPATCH(patch, len)		{if (_seqbufptr) seqbuf_dump();\
+					if (write(seqfd, (char*)(patch), len)==-1) \
+					   perror("Write patch: /dev/sequencer");}
+
+#endif
+long soundcard_init(long mem_start);
+#endif
+#ifndef SOUNDCARD_H
+#define SOUNDCARD_H
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * 
+ */
+
+ /* 
+  * If you make modifications to this file, please contact me before
+  * distributing the modified version. There is already enough 
+  * divercity in the world.
+  *
+  * Regards,
+  * Hannu Savolainen
+  * hsavolai@cs.helsinki.fi
+  */
+
+#define SOUND_VERSION	200
+
+#include <sys/ioctl.h>
+
+/*
+ *	Supported card ID numbers (Should be somewhere else?)
+ */
+
+#define SNDCARD_ADLIB	1
+#define SNDCARD_SB	2
+#define SNDCARD_PAS	3
+#define SNDCARD_GUS	4
+#define SNDCARD_MPU401	5
+
+/***********************************
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#ifndef _IOWR
+/*	@(#)ioctlp.h */
+
+/* Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word.  The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+/* #define	IOCTYPE		(0xff<<8) */
+#define	IOCPARM_MASK	0x7f		/* parameters must be < 128 bytes */
+#define	IOC_VOID	0x20000000	/* no parameters */
+#define	IOC_OUT		0x40000000	/* copy out parameters */
+#define	IOC_IN		0x80000000	/* copy in parameters */
+#define	IOC_INOUT	(IOC_IN|IOC_OUT)
+/* the 0x20000000 is so we can distinguish new ioctl's from old */
+#define	_IO(x,y)	((int)(IOC_VOID|(x<<8)|y))
+#define	_IOR(x,y,t)	((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#define	_IOW(x,y,t)	((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+/* this should be _IORW, but stdio got there first */
+#define	_IOWR(x,y,t)	((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y))
+#endif  /* !_IOWR */
+
+#define SNDCTL_SEQ_RESET		_IO  ('Q', 0)
+#define SNDCTL_SEQ_SYNC			_IO  ('Q', 1)
+#define SNDCTL_SYNTH_INFO		_IOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE		_IOWR('Q', 3, int)	/* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT		_IOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT		_IOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE		_IOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR		_IOW ('Q', 7, struct sbi_instrument)	/* Valid for FM only */
+#define SNDCTL_SEQ_TESTMIDI		_IOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES		_IOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS		_IOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS		_IOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO		_IOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_TRESHOLD		_IOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL		_IOWR('Q',14, int)	/* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE		_IOW ('Q',15, int)	/* in=dev# */
+#define SNDCTL_PMGR_ACCESS		_IOWR('Q',16, struct patmgr_info)
+
+/*
+ *	Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ *	The following patch_info structure has been designed to support
+ *	Gravis UltraSound. It tries to be universal format for uploading
+ *	sample based patches but is propably too limited.
+ */
+
+struct patch_info {
+		short key;		/* Use GUS_PATCH here */
+#define GUS_PATCH	0x04fd
+#define OBSOLETE_GUS_PATCH	0x02fd
+		short device_no;	/* Synthesizer number */
+		short instr_no;		/* Midi pgm# */
+
+		unsigned long mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS	0x01	/* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED	0x02	/* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING	0x04	/* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP	0x08	/* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK	0x10	/* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON	0x20	/* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES	0x40	/* bit 6 = Enable envelopes - 1 */
+				/* 	(use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO	0x00010000	/* The vibrato info is valid */
+#define WAVE_TREMOLO	0x00020000	/* The tremolo info is valid */
+#define WAVE_SCALE	0x00040000	/* The scaling info is valid */
+/* Other bits must be zeroed */
+
+		long len;	/* Size of the wave data in bytes */
+		long loop_start, loop_end; /* Byte offsets from the beginning */
+
+/* 
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for a instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+		unsigned int base_freq;
+		unsigned long base_note;
+		unsigned long high_note;
+		unsigned long low_note;
+		int panning;	/* -128=left, 127=right */
+		int detuning;
+
+/*	New fields introduced in version 1.99.5	*/
+
+       /* Envelope. Enabled by mode bit WAVE_ENVELOPES	*/
+		unsigned char	env_rate[ 6 ];	 /* GUS HW ramping rate */
+		unsigned char	env_offset[ 6 ]; /* 255 == 100% */
+
+	/* 
+	 * The tremolo, vibrato and scale info are not supported yet.
+	 * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+	 * WAVE_SCALE
+	 */
+
+		unsigned char	tremolo_sweep;
+		unsigned char	tremolo_rate;
+		unsigned char	tremolo_depth;
+	
+		unsigned char	vibrato_sweep;
+		unsigned char	vibrato_rate;
+		unsigned char	vibrato_depth;
+
+		int		scale_frequency;
+		unsigned int	scale_factor;		/* from 0 to 2048 or 0 to 2 */
+	
+	        int		volume;
+	        int		spare[4];
+		char data[0];	/* The waveform data starts here */
+	};
+
+
+/*
+ * Patch management interface (/dev/sequencer, /dev/patmgr#)
+ * Don't use these calls if you want to maintain compatibility with
+ * the future versions of the driver.
+ */
+
+#define 	PS_NO_PATCHES		0	/* No patch support on device */
+#define		PS_MGR_NOT_OK		1	/* Plain patch support (no mgr) */
+#define		PS_MGR_OK		2	/* Patch manager supported */
+#define		PS_MANAGED		3	/* Patch manager running */
+
+#define SNDCTL_PMGR_IFACE		_IOWR('P', 1, struct patmgr_info)
+
+/*
+ * The patmgr_info is a fixed size structure which is used for two
+ * different purposes. The intended use is for communication between
+ * the application using /dev/sequencer and the patch manager daemon
+ * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
+ *
+ * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
+ * a patch manager daemon to read and write device parameters. This
+ * ioctl available through /dev/sequencer also. Avoid using it since it's
+ * extremely hardware dependent. In addition access trough /dev/sequencer 
+ * may confuse the patch manager daemon.
+ */
+
+struct patmgr_info {	/* Note! size must be < 4k since kmalloc() is used */
+	  unsigned long key;	/* Don't worry. Reserved for communication
+	  			   between the patch manager and the driver. */
+#define PM_K_EVENT		1 /* Event from the /dev/sequencer driver */
+#define PM_K_COMMAND		2 /* Request from a application */
+#define PM_K_RESPONSE		3 /* From patmgr to application */
+#define PM_ERROR		4 /* Error returned by the patmgr */
+	  int device;
+	  int command;
+
+/* 
+ * Commands 0x000 to 0xfff reserved for patch manager programs 
+ */
+#define PM_GET_DEVTYPE	1	/* Returns type of the patch mgr interface of dev */
+#define		PMTYPE_FM2	1	/* 2 OP fm */
+#define		PMTYPE_FM4	2	/* Mixed 4 or 2 op FM (OPL-3) */
+#define		PMTYPE_WAVE	3	/* Wave table synthesizer (GUS) */
+#define PM_GET_NRPGM	2	/* Returns max # of midi programs in parm1 */
+#define PM_GET_PGMMAP	3	/* Returns map of loaded midi programs in data8 */
+#define PM_GET_PGM_PATCHES 4	/* Return list of patches of a program (parm1) */
+#define PM_GET_PATCH	5	/* Return patch header of patch parm1 */
+#define PM_SET_PATCH	6	/* Set patch header of patch parm1 */
+#define PM_READ_PATCH	7	/* Read patch (wave) data */
+#define PM_WRITE_PATCH	8	/* Write patch (wave) data */
+
+/*
+ * Commands 0x1000 to 0xffff are for communication between the patch manager
+ * and the client
+ */
+#define _PM_LOAD_PATCH	0x100
+
+/* 
+ * Commands above 0xffff reserved for device specific use
+ */
+
+	  long parm1;
+	  long parm2;
+	  long parm3;
+
+	  union {
+		unsigned char data8[4000];
+		unsigned short data16[2000];
+		unsigned long data32[1000];
+		struct patch_info patch;
+	  } data;
+	};
+
+/*
+ * When a patch manager daemon is present, it will be informed by the
+ * driver when something important happens. For example when the
+ * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
+ * returned. The command field contains the event type:
+ */
+#define PM_E_OPENED		1	/* /dev/sequencer opened */
+#define PM_E_CLOSED		2	/* /dev/sequencer closed */
+#define PM_E_PATCH_RESET	3	/* SNDCTL_RESETSAMPLES called */
+#define PM_E_PATCH_LOADED	4	/* A patch has been loaded by appl */
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size. 
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 0xf0,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF		0
+#define SEQ_FMNOTEOFF		SEQ_NOTEOFF	/* Just old name */
+#define SEQ_NOTEON		1
+#define	SEQ_FMNOTEON		SEQ_NOTEON
+#define SEQ_WAIT		2
+#define SEQ_PGMCHANGE		3
+#define SEQ_FMPGMCHANGE		SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER		4
+#define SEQ_MIDIPUTC		5
+#define SEQ_DRUMON		6	/*** OBSOLETE ***/
+#define SEQ_DRUMOFF		7	/*** OBSOLETE ***/
+#define SEQ_ECHO		8	/* For synching programs with output */
+#define SEQ_AFTERTOUCH		9
+#define SEQ_CONTROLLER		10
+#define    CTRL_PITCH_BENDER		255
+#define    CTRL_PITCH_BENDER_RANGE	254
+#define    CTRL_EXPRESSION		253
+#define    CTRL_MAIN_VOLUME		252
+#define SEQ_BALANCE		11
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ *	 input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE		0xfd	/* Long events */
+/*
+ *	SEQ_FULLSIZE events are used for loading patches/samples to the
+ *	synthesizer devices. These events are passed directly to the driver
+ *	of the associated synthesizer device. There is no limit to the size
+ *	of the extended events. These events are not queued but executed
+ *	immediately when the write() is called (execution can take several
+ *	seconds of time). 
+ *
+ *	When a SEQ_FULLSIZE message is written to the device, it must
+ *	be written using exactly one write() call. Other events cannot
+ *	be mixed to the same write.
+ *	
+ *	For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the 
+ *	/dev/sequencer. Don't write other data together with the instrument structure
+ *	Set the key field of the structure to FM_PATCH. The device field is used to
+ *	route the patch to the corresponding device.
+ *
+ *	For Gravis UltraSound use struct patch_info. Initialize the key field
+ *      to GUS_PATCH.
+ */
+#define SEQ_PRIVATE		0xfe	/* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED		0xff	/* Extended events (8 bytes) */
+
+/*
+ *	Extended events for synthesizers (8 bytes)
+ *
+ *	Format:
+ *
+ *		b0	= SEQ_EXTENDED
+ *		b1	= command
+ *		b2	= device
+ *		b3-b7	= parameters
+ *
+ *	Command				b3	b4	b5	b6	b7
+ *	----------------------------------------------------------------------------
+ *	SEQ_NOTEON			voice	note	volume	0	0
+ *	SEQ_NOTEOFF			voice	note	volume	0	0
+ *	SEQ_PGMCHANGE			voice	pgm	0	0	0
+ *	SEQ_DRUMON			(voice)	drum#	volume	0	0
+ *	SEQ_DRUMOFF			(voice)	drum#	volume	0	0
+ */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument {
+		unsigned short	key;		/* 	Initialize to FM_PATCH or OPL3_PATCH */
+#define FM_PATCH	0x01fd
+#define OPL3_PATCH	0x03fd
+		short		device;		/*	Synth# (0-4)	*/
+		int 		channel;	/*	Program# to be initialized 	*/
+		sbi_instr_data	operators;	/*	Register settings for operator cells (.SBI format)	*/
+	};
+
+struct synth_info {	/* Read only */
+		char	name[30];
+		int	device;		/* 0-N. INITIALIZE BEFORE CALLING */
+		int	synth_type;
+#define SYNTH_TYPE_FM			0
+#define SYNTH_TYPE_SAMPLE		1
+
+		int	synth_subtype;
+#define FM_TYPE_ADLIB			0x00
+#define FM_TYPE_OPL3			0x01
+
+#define SAMPLE_TYPE_GUS			0x10
+
+		int	perc_mode;	/* No longer supported */
+		int	nr_voices;
+		int	nr_drums;	/* Obsolete field */
+		int	instr_bank_size;
+		unsigned long	capabilities;	
+#define SYNTH_CAP_PERCMODE		0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3			0x00000002 /* Set if OPL3 supported */
+		int	dummies[19];	/* Reserve space */
+	};
+
+struct midi_info {
+		char		name[30];
+		int		device;		/* 0-N. INITIALIZE BEFORE CALLING */
+		unsigned long	capabilities;	/* To be defined later */
+		int		dummies[19];	/* Reserve space */
+	};
+
+/********************************************
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET		_IO  ('P', 0)
+#define SNDCTL_DSP_SYNC			_IO  ('P', 1)
+#define SNDCTL_DSP_SPEED		_IOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO		_IOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE		_IOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE		_IOWR('P', 5, int)	/* 8, 12 or 16 */
+#define SOUND_PCM_WRITE_CHANNELS	_IOWR('P', 6, int)
+#define SOUND_PCM_WRITE_FILTER		_IOWR('P', 7, int)
+#define SNDCTL_DSP_POST			_IO  ('P', 8)
+
+#define SOUND_PCM_READ_RATE		_IOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS		_IOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS		_IOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER		_IOR ('P', 7, int)
+
+/* Some alias names */
+#define SOUND_PCM_WRITE_BITS		SNDCTL_DSP_SAMPLESIZE
+#define SOUND_PCM_WRITE_RATE		SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST			SNDCTL_DSP_POST
+#define SOUND_PCM_RESET			SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC			SNDCTL_DSP_SYNC
+
+/*********************************************
+ * IOCTL commands for /dev/mixer
+ */
+	
+/* 
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum. 
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES	12
+#define SOUND_MIXER_VOLUME	0
+#define SOUND_MIXER_BASS	1
+#define SOUND_MIXER_TREBLE	2
+#define SOUND_MIXER_SYNTH	3
+#define SOUND_MIXER_PCM		4
+#define SOUND_MIXER_SPEAKER	5
+#define SOUND_MIXER_LINE	6
+#define SOUND_MIXER_MIC		7
+#define SOUND_MIXER_CD		8
+#define SOUND_MIXER_IMIX	9	/*  Recording monitor  */
+#define SOUND_MIXER_ALTPCM	10
+#define SOUND_MIXER_RECLEV	11	/* Recording level */
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN		28
+#define SOUND_ONOFF_MAX		30
+#define SOUND_MIXER_MUTE	28	/* 0 or 1 */
+#define SOUND_MIXER_ENHANCE	29	/* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_LOUD	30	/* 0 or 1 */
+
+/* Note!	Number 31 cannot be used since the sign bit is reserved */
+
+#define SOUND_DEVICE_LABELS	{"Vol  ", "Bass ", "Trebl", "Synth", "Pcm  ", "Spkr ", "Line ", \
+				 "Mic  ", "CD   ", "Mix  ", "Pcm2 ", "rec"}
+
+#define SOUND_DEVICE_NAMES	{"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+				 "mic", "cd", "mix", "pcm2", "rec"}
+
+/*	Device bitmask identifiers	*/
+
+#define SOUND_MIXER_RECSRC	0xff	/* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK	0xfe	/* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK	0xfd	/* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS	0xfc
+	#define SOUND_CAP_EXCL_INPUT	0x00000001	/* Only one recording source at a time */
+#define SOUND_MIXER_STEREODEVS	0xfb	/* Mixer channels supporting stereo */
+
+/*	Device mask bits	*/
+
+#define SOUND_MASK_VOLUME	(1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS		(1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE	(1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH	(1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM		(1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER	(1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE		(1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC		(1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD		(1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX		(1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM	(1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV	(1 << SOUND_MIXER_RECLEV)
+
+#define SOUND_MASK_MUTE		(1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE	(1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD		(1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev)		_IOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME		MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS		MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE		MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH		MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM		MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER	MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE		MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC		MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD		MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX		MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM		MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV		MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_MUTE		MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE	MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD		MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC		MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK	MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK	MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS	MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS		MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev)		_IOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME	MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS		MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE	MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH		MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM		MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER	MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE		MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC		MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD		MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX		MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM	MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV	MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_MUTE		MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE	MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD		MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC	MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/*
+ *	The following mixer ioctl calls are compatible with the BSD driver by
+ *	  Steve Haehnichen <shaehnic@ucsd.edu>
+ *
+ * Since this interface is entirely SB specific, it will be dropped in the
+ * near future.
+ */
+
+typedef unsigned char S_BYTE;
+typedef unsigned char S_FLAG;
+struct stereo_vol
+{
+  S_BYTE l;			/* Left volume */
+  S_BYTE r;			/* Right volume */
+};
+
+#define MIXER_IOCTL_SET_LEVELS 		_IOW ('s', 20, struct sb_mixer_levels)
+#define MIXER_IOCTL_SET_PARAMS 		_IOW ('s', 21, struct sb_mixer_params)
+#define MIXER_IOCTL_READ_LEVELS 	_IOR ('s', 22, struct sb_mixer_levels)
+#define MIXER_IOCTL_READ_PARAMS 	_IOR ('s', 23, struct sb_mixer_params)
+#define MIXER_IOCTL_RESET		_IO  ('s', 24)
+
+/*
+ * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
+ */
+struct sb_mixer_levels
+{
+  struct stereo_vol master;	/* Master volume */
+  struct stereo_vol voc;	/* DSP Voice volume */
+  struct stereo_vol fm;		/* FM volume */
+  struct stereo_vol line;	/* Line-in volume */
+  struct stereo_vol cd;		/* CD audio */
+  S_BYTE mic;			/* Microphone level */
+};
+
+/*
+ * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
+ */
+struct sb_mixer_params
+{
+  S_BYTE record_source;		/* Recording source (See SRC_xxx below) */
+  S_FLAG hifreq_filter;		/* Filter frequency (hi/low) */
+  S_FLAG filter_input;		/* ANFI input filter */
+  S_FLAG filter_output;		/* DNFI output filter */
+  S_FLAG dsp_stereo;		/* 1 if DSP is in Stereo mode */
+};
+
+#define SRC_MIC         1	/* Select Microphone recording source */
+#define SRC_CD          3	/* Select CD recording source */
+#define SRC_LINE        7	/* Use Line-in for recording source */
+
+#if !defined(KERNEL) && !defined(INKERNEL)
+/*
+ *	Some convenience macros to simplify programming of the
+ *	/dev/sequencer interface
+ *
+ *	These macros define the API which should be used when possible.
+ */
+
+void seqbuf_dump(void);	/* This function must be provided by programs */
+
+/* Sample seqbuf_dump() implementation:
+ *
+ *	SEQ_DEFINEBUF (2048);	-- Defines a buffer for 2048 bytes
+ *
+ *	int seqfd;		-- The file descriptor for /dev/sequencer.
+ *
+ *	void
+ *	seqbuf_dump ()
+ *	{
+ *	  if (_seqbufptr)
+ *	    if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ *	      {
+ *		perror ("write /dev/sequencer");
+ *		exit (-1);
+ *	      }
+ *	  _seqbufptr = 0;
+ *	}
+ */
+
+#define SEQ_DEFINEBUF(len)		unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0
+#define SEQ_PM_DEFINES			struct patmgr_info _pm_info
+#define _SEQ_NEEDBUF(len)		if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len)		_seqbufptr += len
+#define SEQ_DUMPBUF			seqbuf_dump
+#define PM_LOAD_PATCH(dev, bank, pgm)	(SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+					_pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
+					_pm_info.parm1 = bank, _pm_info.parm2 = 1, \
+					ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+					_pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \
+					_pm_info.parm1 = bank, _pm_info.parm2 = 128, \
+					ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+
+#define SEQ_START_NOTE(dev, voice, note, vol)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_NOTEON;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (note);\
+					_seqbuf[_seqbufptr+5] = (vol);\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_STOP_NOTE(dev, voice, note, vol)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (note);\
+					_seqbuf[_seqbufptr+5] = (vol);\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, voice, pressure)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (pressure);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_PANNING(dev, voice, pos)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_BALANCE;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					(char)_seqbuf[_seqbufptr+4] = (pos);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_CONTROL(dev, voice, controller, value)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (controller);\
+					*(short *)&_seqbuf[_seqbufptr+5] = (value);\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value)
+#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value)
+
+#define SEQ_START_TIMER()		{_SEQ_NEEDBUF(4);\
+					_seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\
+					_seqbuf[_seqbufptr+1] = 0;\
+					_seqbuf[_seqbufptr+2] = 0;\
+					_seqbuf[_seqbufptr+3] = 0;\
+					_SEQ_ADVBUF(4);}
+#define SEQ_SET_PATCH(dev, voice, patch)	{_SEQ_NEEDBUF(8);\
+					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+					_seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\
+					_seqbuf[_seqbufptr+2] = (dev);\
+					_seqbuf[_seqbufptr+3] = (voice);\
+					_seqbuf[_seqbufptr+4] = (patch);\
+					_seqbuf[_seqbufptr+5] = 0;\
+					_seqbuf[_seqbufptr+6] = 0;\
+					_seqbuf[_seqbufptr+7] = 0;\
+					_SEQ_ADVBUF(8);}
+
+#define SEQ_WAIT_TIME(ticks)		{_SEQ_NEEDBUF(4);\
+				 	*(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\
+				 	_SEQ_ADVBUF(4);}
+
+#define SEQ_ECHO_BACK(key)		{_SEQ_NEEDBUF(4);\
+				 	*(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\
+				 	_SEQ_ADVBUF(4);}
+
+#define SEQ_MIDIOUT(device, byte)	{_SEQ_NEEDBUF(4);\
+					_seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+					_seqbuf[_seqbufptr+1] = (byte);\
+					_seqbuf[_seqbufptr+2] = (device);\
+					_seqbuf[_seqbufptr+3] = 0;\
+					_SEQ_ADVBUF(4);}
+#define SEQ_WRPATCH(patch, len)		{if (_seqbufptr) seqbuf_dump();\
+					if (write(seqfd, (char*)(patch), len)==-1) \
+					   perror("Write patch: /dev/sequencer");}
+
+#endif
+long soundcard_init(long mem_start);
+#endif
diff --git a/drivers/sound/tuning.h b/drivers/sound/tuning.h
new file mode 100644
index 0000000..858e1fe
--- /dev/null
+++ b/drivers/sound/tuning.h
@@ -0,0 +1,29 @@
+#ifdef SEQUENCER_C
+
+unsigned short semitone_tuning[24] = 
+{
+/*   0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, 
+/*   8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, 
+/*  16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+unsigned short cent_tuning[100] =
+{
+/*   0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, 
+/*   8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, 
+/*  16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, 
+/*  24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, 
+/*  32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, 
+/*  40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, 
+/*  48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, 
+/*  56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, 
+/*  64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, 
+/*  72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, 
+/*  80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, 
+/*  88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, 
+/*  96 */ 10570, 10576, 10582, 10589
+};
+#else
+extern unsigned short semitone_tuning[24];
+extern unsigned short cent_tuning[100];
+#endif
diff --git a/drivers/sound/ulaw.h b/drivers/sound/ulaw.h
new file mode 100644
index 0000000..be9f92d
--- /dev/null
+++ b/drivers/sound/ulaw.h
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+     0,    0,    0,    0,    0,    0,    0,    0, 
+     0,    0,    0,    0,    0,    0,    0,    0, 
+     0,    0,    0,    0,    0,    0,    0,    0, 
+     0,    0,    0,    0,    0,    0,    0,    2, 
+     5,    9,   13,   17,   21,   25,   29,   33, 
+    37,   41,   45,   49,   53,   57,   61,   65, 
+    68,   70,   72,   74,   76,   78,   80,   82, 
+    84,   86,   88,   90,   92,   94,   96,   98, 
+   100,  101,  102,  103,  104,  105,  106,  107, 
+   108,  109,  110,  111,  112,  113,  114,  115, 
+   115,  116,  116,  117,  117,  118,  118,  119, 
+   119,  120,  120,  121,  121,  122,  122,  123, 
+   123,  123,  124,  124,  124,  124,  125,  125, 
+   125,  125,  126,  126,  126,  126,  127,  127, 
+   127,  127,  127,  127,  128,  128,  128,  128, 
+   128,  128,  128,  128,  128,  128,  128,  128, 
+   255,  255,  255,  255,  255,  255,  255,  255, 
+   255,  255,  255,  255,  255,  255,  255,  255, 
+   255,  255,  255,  255,  255,  255,  255,  255, 
+   255,  255,  255,  255,  255,  255,  255,  255, 
+   252,  248,  244,  240,  236,  232,  228,  224, 
+   220,  216,  212,  208,  204,  200,  196,  192, 
+   189,  187,  185,  183,  181,  179,  177,  175, 
+   173,  171,  169,  167,  165,  163,  161,  159, 
+   157,  156,  155,  154,  153,  152,  151,  150, 
+   149,  148,  147,  146,  145,  144,  143,  142, 
+   142,  141,  141,  140,  140,  139,  139,  138, 
+   138,  137,  137,  136,  136,  135,  135,  134, 
+   134,  134,  133,  133,  133,  133,  132,  132, 
+   132,  132,  131,  131,  131,  131,  130,  130, 
+   130,  130,  130,  130,  129,  129,  129,  129, 
+   129,  129,  129,  129,  128,  128,  128,  128, 
+};
+
+static unsigned char dsp_ulaw[] = {
+    31,   31,   31,   32,   32,   32,   32,   33, 
+    33,   33,   33,   34,   34,   34,   34,   35, 
+    35,   35,   35,   36,   36,   36,   36,   37, 
+    37,   37,   37,   38,   38,   38,   38,   39, 
+    39,   39,   39,   40,   40,   40,   40,   41, 
+    41,   41,   41,   42,   42,   42,   42,   43, 
+    43,   43,   43,   44,   44,   44,   44,   45, 
+    45,   45,   45,   46,   46,   46,   46,   47, 
+    47,   47,   47,   48,   48,   49,   49,   50, 
+    50,   51,   51,   52,   52,   53,   53,   54, 
+    54,   55,   55,   56,   56,   57,   57,   58, 
+    58,   59,   59,   60,   60,   61,   61,   62, 
+    62,   63,   63,   64,   65,   66,   67,   68, 
+    69,   70,   71,   72,   73,   74,   75,   76, 
+    77,   78,   79,   81,   83,   85,   87,   89, 
+    91,   93,   95,   99,  103,  107,  111,  119, 
+   255,  247,  239,  235,  231,  227,  223,  221, 
+   219,  217,  215,  213,  211,  209,  207,  206, 
+   205,  204,  203,  202,  201,  200,  199,  198, 
+   197,  196,  195,  194,  193,  192,  191,  191, 
+   190,  190,  189,  189,  188,  188,  187,  187, 
+   186,  186,  185,  185,  184,  184,  183,  183, 
+   182,  182,  181,  181,  180,  180,  179,  179, 
+   178,  178,  177,  177,  176,  176,  175,  175, 
+   175,  175,  174,  174,  174,  174,  173,  173, 
+   173,  173,  172,  172,  172,  172,  171,  171, 
+   171,  171,  170,  170,  170,  170,  169,  169, 
+   169,  169,  168,  168,  168,  168,  167,  167, 
+   167,  167,  166,  166,  166,  166,  165,  165, 
+   165,  165,  164,  164,  164,  164,  163,  163, 
+   163,  163,  162,  162,  162,  162,  161,  161, 
+   161,  161,  160,  160,  160,  160,  159,  159, 
+};
diff --git a/drivers/sound/ultrasound.h b/drivers/sound/ultrasound.h
new file mode 100644
index 0000000..7d8b57d
--- /dev/null
+++ b/drivers/sound/ultrasound.h
@@ -0,0 +1,120 @@
+#ifndef _ULTRASOUND_H_
+#define _ULTRASOUND_H_
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ *	ultrasound.h - Macros for programming the Gravis Ultrasound
+ *			These macros are extremely device dependent
+ *			and not portable.
+ */
+
+#include "soundcard.h"
+
+/*
+ *	Private events for Gravis Ultrasound (GUS)
+ *
+ *	Format:
+ *		byte 0 		- SEQ_PRIVATE (0xfe)
+ *		byte 1 		- Synthesizer device number (0-N)
+ *		byte 2 		- Command (see below)
+ *		byte 3 		- Voice number (0-31)
+ *		bytes 4 and 5	- parameter P1 (unsigned short)
+ *		bytes 6 and 7	- parameter P2 (unsigned short)
+ *
+ *	Commands:
+ *		Each command affects one voice defined in byte 3.
+ *		Unused parameters (P1 and/or P2 *MUST* be initialized to zero).
+ *		_GUS_NUMVOICES	- Sets max. number of concurrent voices (P1=14-31, default 16)
+ *		_GUS_VOICESAMPLE- ************ OBSOLETE *************
+ *		_GUS_VOICEON	- Starts voice (P1=voice mode)
+ *		_GUS_VOICEOFF	- Stops voice (no parameters)
+ *		_GUS_VOICEFADE	- Stops the voice smoothly.
+ *		_GUS_VOICEMODE	- Alters the voice mode, don't start or stop voice (P1=voice mode)
+ *		_GUS_VOICEBALA	- Sets voice balence (P1, 0=left, 7=middle and 15=right, default 7)
+ *		_GUS_VOICEFREQ	- Sets voice (sample) playback frequency (P1=Hz)
+ *		_GUS_VOICEVOL	- Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ *		_GUS_VOICEVOL2	- Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ *				  (Like GUS_VOICEVOL but doesn't change the hw
+ *				  volume. It just updates volume in the voice table).
+ *
+ *		_GUS_RAMPRANGE	- Sets limits for volume ramping (P1=low volume, P2=high volume)
+ *		_GUS_RAMPRATE	- Sets the speed for volume ramping (P1=scale, P2=rate)
+ *		_GUS_RAMPMODE	- Sets the volume ramping mode (P1=ramping mode)
+ *		_GUS_RAMPON	- Starts volume ramping (no parameters)
+ *		_GUS_RAMPOFF	- Stops volume ramping (no parameters)
+ *		_GUS_VOLUME_SCALE - Changes the volume calculation constants
+ *				  for all voices.
+ */
+
+#define _GUS_NUMVOICES		0x00
+#define _GUS_VOICESAMPLE	0x01	/* OBSOLETE */
+#define _GUS_VOICEON		0x02
+#define _GUS_VOICEOFF		0x03
+#define _GUS_VOICEMODE		0x04
+#define _GUS_VOICEBALA		0x05
+#define _GUS_VOICEFREQ		0x06
+#define _GUS_VOICEVOL		0x07
+#define _GUS_RAMPRANGE		0x08
+#define _GUS_RAMPRATE		0x09
+#define _GUS_RAMPMODE		0x0a
+#define _GUS_RAMPON		0x0b
+#define _GUS_RAMPOFF		0x0c
+#define _GUS_VOICEFADE		0x0d
+#define _GUS_VOLUME_SCALE	0x0e
+#define _GUS_VOICEVOL2		0x0f
+
+/*
+ *	GUS API macros
+ */
+
+#define _GUS_CMD(chn, voice, cmd, p1, p2) \
+					{_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
+					_seqbuf[_seqbufptr+1] = (chn); _seqbuf[_seqbufptr+2] = cmd;\
+					_seqbuf[_seqbufptr+3] = voice;\
+					*(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
+					*(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
+					_SEQ_ADVBUF(8);}
+
+#define GUS_NUMVOICES(chn, p1)			_GUS_CMD(chn, 0, _GUS_NUMVOICES, (p1), 0)
+#define GUS_VOICESAMPLE(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICESAMPLE, (p1), 0)	/* OBSOLETE */
+#define GUS_VOICEON(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICEON, (p1), 0)
+#define GUS_VOICEOFF(chn, voice)		_GUS_CMD(chn, voice, _GUS_VOICEOFF, 0, 0)
+#define GUS_VOICEFADE(chn, voice)		_GUS_CMD(chn, voice, _GUS_VOICEFADE, 0, 0)
+#define GUS_VOICEMODE(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICEMODE, (p1), 0)
+#define GUS_VOICEBALA(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICEBALA, (p1), 0)
+#define GUS_VOICEFREQ(chn, voice, p)		_GUS_CMD(chn, voice, _GUS_VOICEFREQ, \
+							(p) & 0xffff, ((p) >> 16) & 0xffff)
+#define GUS_VOICEVOL(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICEVOL, (p1), 0)
+#define GUS_VOICEVOL2(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_VOICEVOL2, (p1), 0)
+#define GUS_RAMPRANGE(chn, voice, low, high)	_GUS_CMD(chn, voice, _GUS_RAMPRANGE, (low), (high))
+#define GUS_RAMPRATE(chn, voice, p1, p2)	_GUS_CMD(chn, voice, _GUS_RAMPRATE, (p1), (p2))
+#define GUS_RAMPMODE(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_RAMPMODE, (p1), 0)
+#define GUS_RAMPON(chn, voice, p1)		_GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0)
+#define GUS_RAMPOFF(chn, voice)			_GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0)
+#define GUS_VOLUME_SCALE(chn, voice, p1, p2)	_GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2))
+
+#endif
diff --git a/fs/Makefile b/fs/Makefile
index c0b5c62..146d7bf 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -7,7 +7,7 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs
+SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs hpfs
 
 ifdef CONFIG_MINIX_FS
 FS_SUBDIRS := $(FS_SUBDIRS) minix
@@ -33,6 +33,9 @@
 ifdef CONFIG_XIA_FS
 FS_SUBDIRS := $(FS_SUBDIRS) xiafs
 endif
+ifdef CONFIG_HPFS_FS
+FS_SUBDIRS := $(FS_SUBDIRS) hpfs
+endif
 
 ifdef CONFIG_BINFMT_ELF
 BINFMTS := $(BINFMTS) binfmt_elf.o
@@ -56,17 +59,14 @@
 
 filesystems.a: dummy
 	rm -f filesystems.a
-	@for i in $(FS_SUBDIRS); do [ ! -d $$i ] || \
-	(cd $$i && echo $$i && $(MAKE) && $(AR) rcs ../filesystems.a $$i.o) \
-	|| exit; done
-
-clean:
-	rm -f core *.s *.o *.a
-	for i in $(SUBDIRS); do ([ -d $$i ] && cd $$i && $(MAKE) clean); done
+	set -e; for i in $(FS_SUBDIRS); do \
+	  test ! -d $$i || \
+	    { $(MAKE) -C $$i; $(AR) rcs filesystems.a $$i/$$i.o; }; done
 
 depend dep:
 	$(CPP) -M *.c > .depend
-	for i in $(SUBDIRS); do [ ! -d $$i ] || (cd $$i && $(MAKE) dep) || exit; done
+	set -e; for i in $(SUBDIRS); do \
+	  test ! -d $$i || $(MAKE) -C $$i dep; done
 
 dummy:
 
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index bb02f09..4a7a1c7 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -326,28 +326,11 @@
 
 	regs->eip = elf_entry;		/* eip, magic happens :-) */
 	regs->esp = bprm->p;			/* stack pointer */
-	{
-		struct vm_area_struct *mpnt;
-
-		mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
-		
-		mpnt->vm_task = current;
-		mpnt->vm_start = bprm->p & PAGE_MASK;
-		mpnt->vm_end = TASK_SIZE;
-		mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY;
-		mpnt->vm_share = NULL;
-		mpnt->vm_inode = NULL;
-		mpnt->vm_offset = 0;
-		mpnt->vm_ops = NULL;
-		insert_vm_struct(current, mpnt);
-		current->stk_vma = mpnt;
-	}
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
 	return 0;
 }
 
-
 /* This is really simpleminded and specialized - we are loading an a.out library that is given
    an ELF header */
 
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 686c069..9a839c6 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -21,7 +21,7 @@
 	int offset;
 	int chars;
 	int written = 0;
-	int size;
+	unsigned int size;
 	unsigned int dev;
 	struct buffer_head * bh;
 	register char * p;
@@ -81,7 +81,8 @@
 	unsigned int offset;
 	int blocksize;
 	int blocksize_bits, i;
-	int blocks, left;
+	unsigned int left;
+	int blocks;
 	int bhrequest, uptodate;
 	struct buffer_head ** bhb, ** bhe;
 	struct buffer_head * buflist[NBUF];
diff --git a/fs/buffer.c b/fs/buffer.c
index 17d5f64..1d00868 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -22,6 +22,7 @@
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/errno.h>
@@ -222,7 +223,7 @@
 	struct buffer_head * bh;
 
 	switch(MAJOR(dev)){
-	case 2: /* floppy disc */
+	case FLOPPY_MAJOR:
 		if (!(bh = getblk(dev,0,1024)))
 			return;
 		i = floppy_change(bh);
@@ -230,25 +231,25 @@
 		break;
 
 #if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
-         case 8: /* Removable scsi disk */
+         case SCSI_DISK_MAJOR:
 		i = check_scsidisk_media_change(dev, 0);
 		break;
 #endif
 
 #if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
-         case 11: /* CDROM */
+	 case SCSI_CDROM_MAJOR:
 		i = check_cdrom_media_change(dev, 0);
 		break;
 #endif
 
 #if defined(CONFIG_CDU31A)
-         case 15: /* Sony CDROM */
+         case CDU31A_CDROM_MAJOR:
 		i = check_cdu31a_media_change(dev, 0);
 		break;
 #endif
 
 #if defined(CONFIG_MCD)
-         case 23: /* Sony CDROM */
+         case MITSUMI_CDROM_MAJOR:
 		i = check_mcd_media_change(dev, 0);
 		break;
 #endif
@@ -270,7 +271,7 @@
 #if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
 /* This is trickier for a removable hardisk, because we have to invalidate
    all of the partitions that lie on the disk. */
-	if (MAJOR(dev) == 8)
+	if (MAJOR(dev) == SCSI_DISK_MAJOR)
 		revalidate_scsidisk(dev, 0);
 #endif
 }
@@ -505,7 +506,7 @@
 /* already have added "this" block to the cache. check it */
 	if (find_buffer(dev,block,size))
 		goto repeat;
-/* OK, FINALLY we know that this buffer is the only one of it's kind, */
+/* OK, FINALLY we know that this buffer is the only one of its kind, */
 /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
 	bh->b_count=1;
 	bh->b_dirt=0;
@@ -848,7 +849,7 @@
 		if (b[i])
 			bh[i] = getblk(dev, b[i], size);
 	}
-	read_buffers(bh,4);
+	read_buffers(bh,i);
 	where = address;
  	for (i=0, j=0; j<PAGE_SIZE ; i++, j += size,address += size) {
 		if (bh[i]) {
diff --git a/fs/devices.c b/fs/devices.c
index b3bb1c5..02b5cfa 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/major.h>
 #include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/ext_fs.h>
@@ -15,21 +16,41 @@
 #include <linux/fcntl.h>
 #include <linux/errno.h>
 
-struct file_operations * chrdev_fops[MAX_CHRDEV] = {
-	NULL,
+struct device_struct {
+	const char * name;
+	struct file_operations * fops;
 };
 
-struct file_operations * blkdev_fops[MAX_BLKDEV] = {
-	NULL,
+static struct device_struct chrdevs[MAX_CHRDEV] = {
+	{ NULL, NULL },
 };
 
+static struct device_struct blkdevs[MAX_BLKDEV] = {
+	{ NULL, NULL },
+};
+
+struct file_operations * get_blkfops(unsigned int major)
+{
+	if (major >= MAX_BLKDEV)
+		return NULL;
+	return blkdevs[major].fops;
+}
+
+struct file_operations * get_chrfops(unsigned int major)
+{
+	if (major >= MAX_CHRDEV)
+		return NULL;
+	return chrdevs[major].fops;
+}
+
 int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
 {
 	if (major >= MAX_CHRDEV)
 		return -EINVAL;
-	if (chrdev_fops[major])
+	if (chrdevs[major].fops)
 		return -EBUSY;
-	chrdev_fops[major] = fops;
+	chrdevs[major].name = name;
+	chrdevs[major].fops = fops;
 	return 0;
 }
 
@@ -37,9 +58,36 @@
 {
 	if (major >= MAX_BLKDEV)
 		return -EINVAL;
-	if (blkdev_fops[major])
+	if (blkdevs[major].fops)
 		return -EBUSY;
-	blkdev_fops[major] = fops;
+	blkdevs[major].name = name;
+	blkdevs[major].fops = fops;
+	return 0;
+}
+
+int unregister_chrdev(unsigned int major, const char * name)
+{
+	if (major >= MAX_CHRDEV)
+		return -EINVAL;
+	if (!chrdevs[major].fops)
+		return -EINVAL;
+	if (strcmp(chrdevs[major].name, name))
+		return -EINVAL;
+	chrdevs[major].name = NULL;
+	chrdevs[major].fops = NULL;
+	return 0;
+}
+
+int unregister_blkdev(unsigned int major, const char * name)
+{
+	if (major >= MAX_BLKDEV)
+		return -EINVAL;
+	if (!blkdevs[major].fops)
+		return -EINVAL;
+	if (strcmp(blkdevs[major].name, name))
+		return -EINVAL;
+	blkdevs[major].name = NULL;
+	blkdevs[major].fops = NULL;
 	return 0;
 }
 
@@ -51,9 +99,9 @@
 	int i;
 
 	i = MAJOR(inode->i_rdev);
-	if (i >= MAX_BLKDEV || !blkdev_fops[i])
+	if (i >= MAX_BLKDEV || !blkdevs[i].fops)
 		return -ENODEV;
-	filp->f_op = blkdev_fops[i];
+	filp->f_op = blkdevs[i].fops;
 	if (filp->f_op->open)
 		return filp->f_op->open(inode,filp);
 	return 0;
@@ -102,9 +150,9 @@
 	int i;
 
 	i = MAJOR(inode->i_rdev);
-	if (i >= MAX_CHRDEV || !chrdev_fops[i])
+	if (i >= MAX_CHRDEV || !chrdevs[i].fops)
 		return -ENODEV;
-	filp->f_op = chrdev_fops[i];
+	filp->f_op = chrdevs[i].fops;
 	if (filp->f_op->open)
 		return filp->f_op->open(inode,filp);
 	return 0;
diff --git a/fs/exec.c b/fs/exec.c
index a4c3e31..741da6b 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -265,7 +265,21 @@
 {
 	unsigned long *argv,*envp;
 	unsigned long * sp;
+	struct vm_area_struct *mpnt;
 
+	mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
+	if (mpnt) {
+		mpnt->vm_task = current;
+		mpnt->vm_start = PAGE_MASK & (unsigned long) p;
+		mpnt->vm_end = TASK_SIZE;
+		mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY;
+		mpnt->vm_share = NULL;
+		mpnt->vm_inode = NULL;
+		mpnt->vm_offset = 0;
+		mpnt->vm_ops = NULL;
+		insert_vm_struct(current, mpnt);
+		current->stk_vma = mpnt;
+	}
 	sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
 	sp -= envc+1;
 	envp = sp;
@@ -422,8 +436,11 @@
  			goto close_readexec;
 	} else
 		file.f_pos = offset;
-	if (get_fs() == USER_DS)
-		verify_area(VERIFY_WRITE, addr, count);
+	if (get_fs() == USER_DS) {
+		result = verify_area(VERIFY_WRITE, addr, count);
+		if (result)
+			goto close_readexec;
+	}
 	result = file.f_op->read(inode, &file, addr, count);
 close_readexec:
 	if (file.f_op->release)
@@ -815,22 +832,6 @@
 	current->start_stack = p;
 	regs->eip = ex.a_entry;		/* eip, magic happens :-) */
 	regs->esp = p;			/* stack pointer */
-	{
-		struct vm_area_struct *mpnt;
-
-		mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
-		
-		mpnt->vm_task = current;
-		mpnt->vm_start = p & PAGE_MASK;
-		mpnt->vm_end = TASK_SIZE;
-		mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY;
-		mpnt->vm_share = NULL;
-		mpnt->vm_inode = NULL;
-		mpnt->vm_offset = 0;
-		mpnt->vm_ops = NULL;
-		insert_vm_struct(current, mpnt);
-		current->stk_vma = mpnt;
-	}
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
 	return 0;
diff --git a/fs/ext/Makefile b/fs/ext/Makefile
index 2a9bf99..5e23319 100644
--- a/fs/ext/Makefile
+++ b/fs/ext/Makefile
@@ -20,9 +20,6 @@
 ext.o: $(OBJS)
 	$(LD) -r -o ext.o $(OBJS)
 
-clean:
-	rm -f core *.s *.o *.a
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/ext/file.c b/fs/ext/file.c
index 125461d..d8caabd 100644
--- a/fs/ext/file.c
+++ b/fs/ext/file.c
@@ -250,8 +250,7 @@
 		bh->b_dirt = 1;
 		brelse(bh);
 	}
-	inode->i_mtime = CURRENT_TIME;
-	inode->i_ctime = CURRENT_TIME;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	filp->f_pos = pos;
 	inode->i_dirt = 1;
 	return written;
diff --git a/fs/ext/namei.c b/fs/ext/namei.c
index f0add8c..a8bc1cb 100644
--- a/fs/ext/namei.c
+++ b/fs/ext/namei.c
@@ -246,7 +246,9 @@
 					offset += de->rec_len;
 					dir->i_size += de->rec_len;
 					dir->i_dirt = 1;
+#if 0
 					dir->i_ctime = CURRENT_TIME;
+#endif
 					bh->b_dirt = 1;
 				}
 				brelse (bh);
@@ -264,7 +266,9 @@
 			de->rec_len = rec_len;
 			dir->i_size += de->rec_len;
 			dir->i_dirt = 1;
+#if 0
 			dir->i_ctime = CURRENT_TIME;
+#endif
 		}
 		if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
 		    de->rec_len < de->name_len + 8 ||
@@ -288,7 +292,7 @@
 				de1->name_len = 0;
 				de->rec_len = rec_len;
 			}
-			dir->i_mtime = CURRENT_TIME;
+			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			de->name_len = namelen;
 			for (i=0; i < namelen ; i++)
 				de->name[i] = name[i];
@@ -376,7 +380,9 @@
 		init_fifo(inode);
 	if (S_ISBLK(mode) || S_ISCHR(mode))
 		inode->i_rdev = rdev;
+#if 0
 	inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
 	inode->i_dirt = 1;
 	bh = ext_add_entry(dir,name,len,&de);
 	if (!bh) {
@@ -417,7 +423,9 @@
 					- 2 bytes for the record length
 					- 2 bytes for the name length
 					- 8 bytes for the name */
+#if 0
 	inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
 	dir_block = ext_bread(inode,0,1);
 	if (!dir_block) {
 		iput(dir);
@@ -563,7 +571,7 @@
 	inode->i_nlink=0;
 	inode->i_dirt=1;
 	dir->i_nlink--;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt=1;
 	retval = 0;
 end_rmdir:
@@ -606,7 +614,7 @@
 	inode->i_nlink--;
 	inode->i_dirt = 1;
 	inode->i_ctime = CURRENT_TIME;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_ctime = dir->i_mtime = inode->i_ctime;
 	dir->i_dirt = 1;
 	retval = 0;
 end_unlink:
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
index dd04ba7..f8f8e95 100644
--- a/fs/ext2/Makefile
+++ b/fs/ext2/Makefile
@@ -14,15 +14,12 @@
 .s.o:
 	$(AS) -o $*.o $<
 
-OBJS=	acl.o balloc.o bitmap.o dcache.o dir.o file.o fsync.o ialloc.o inode.o \
-	ioctl.o namei.o symlink.o truncate.o
+OBJS=	acl.o balloc.o bitmap.o dcache.o dir.o file.o fsync.o \
+	ialloc.o inode.o ioctl.o namei.o super.o symlink.o truncate.o
 
 ext2.o: $(OBJS)
 	$(LD) -r -o ext2.o $(OBJS)
 
-clean:
-	rm -f core *.s *.o *.a
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index ae4d89b..2103f81 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -9,9 +9,11 @@
  * second extended file system.
  */
 
-#include <linux/sched.h>
-#include <linux/ext2_fs.h>
 #include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/stat.h>
 
 /*
@@ -21,7 +23,7 @@
  */
 int ext2_permission (struct inode * inode, int mask)
 {
-	int mode = inode->i_mode;
+	unsigned short mode = inode->i_mode;
 
 	/* Special case, access is always granted for root */
 	if (suser ())
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index be9b8b7..d5cf2d0 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -21,10 +21,11 @@
 
 */
 
-#include <linux/sched.h>
+#include <linux/fs.h>
 #include <linux/ext2_fs.h>
-#include <linux/stat.h>
 #include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/locks.h>
 
@@ -41,6 +42,7 @@
 static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
 {
 	int res;
+
 	if (!size)
 		return 0;
 	__asm__("
@@ -91,6 +93,7 @@
 static inline char * find_first_zero_byte (char * addr, int size)
 {
 	char *res;
+
 	if (!size)
 		return 0;
 	__asm__("
@@ -117,21 +120,21 @@
 	
 	group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
 	desc = block_group % EXT2_DESC_PER_BLOCK(sb);
-	if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
-		printk ("block_group = %d,group_desc = %d,desc = %d\n",
-			 block_group, group_desc, desc);
-		panic ("read_block_bitmap: Group descriptor not loaded");
-	}
+	if (!sb->u.ext2_sb.s_group_desc[group_desc])
+		ext2_panic (sb, "read_block_bitmap",
+			    "Group descriptor not loaded\n"
+			    "block_group = %d, group_desc = %lu, desc = %lu",
+			     block_group, group_desc, desc);
 	gdp = (struct ext2_group_desc *) 
 		sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
 	bh = bread (sb->s_dev, gdp[desc].bg_block_bitmap, sb->s_blocksize);
-	if (!bh) {
-		printk ("block_group = %d,group_desc = %d,"
-			"desc = %d,block_bitmap = %d\n",
-			block_group, group_desc, desc,
-			gdp[desc].bg_block_bitmap);
-		panic ("read_block_bitmap: Cannot read block bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "read_block_bitmap",
+			    "Cannot read block bitmap\n"
+			    "block_group = %d, group_desc = %lu,"
+			    "desc = %lu, block_bitmap = %lu",
+			    block_group, group_desc, desc,
+			    gdp[desc].bg_block_bitmap);
 	sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
 }
@@ -154,18 +157,18 @@
 	unsigned long block_bitmap_number;
 	struct buffer_head * block_bitmap;
 
-	if (block_group >= sb->u.ext2_sb.s_groups_count) {
-		printk ("block_group = %d, groups_count = %d\n",
-			block_group, sb->u.ext2_sb.s_groups_count);
-		panic ("load_block_bitmap: block_group >= groups_count");
-	}
+	if (block_group >= sb->u.ext2_sb.s_groups_count)
+		ext2_panic (sb, "load_block_bitmap",
+			    "block_group >= groups_count\n"
+			    "block_group = %d, groups_count = %lu",
+			    block_group, sb->u.ext2_sb.s_groups_count);
 
 	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
 		if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
 			if (sb->u.ext2_sb.s_block_bitmap_number[block_group] !=
 			    block_group)
-				panic ("load_block_bitmap: "
-				       "block_group != block_bitmap_number");
+				ext2_panic (sb, "load_block_bitmap",
+					    "block_group != block_bitmap_number");
 			else
 				return block_group;
 		} else {
@@ -235,50 +238,56 @@
 	struct ext2_super_block * es;
 
 	if (!sb) {
-		printk ("ext2_free_block: nonexistant device");
+		printk ("ext2_free_block: nonexistent device");
 		return;
 	}
 	lock_super (sb);
 	es = sb->u.ext2_sb.s_es;
 	if (block < es->s_first_data_block || block >= es->s_blocks_count) {
-		printk ("ext2_free_block: block not in datazone\n");
+		ext2_error (sb, "ext2_free_block", "block not in datazone");
 		unlock_super (sb);
 		return;
 	}
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_free_block: freeing block %d\n", block);
-#endif
+
+	ext2_debug ("freeing block %lu\n", block);
+
+#if 0	/* XXX - This is incompatible with the secure rm implemented in 0.4 */
 	bh = get_hash_table (sb->s_dev, block, sb->s_blocksize);
 	if (bh)
 		bh->b_dirt = 0;
 	brelse (bh);
+#endif
 	block_group = (block - es->s_first_data_block) /
 		      EXT2_BLOCKS_PER_GROUP(sb);
 	bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
 	bitmap_nr = load_block_bitmap (sb, block_group);
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
-	if (!bh) {
-		printk ("block_group = %d\n", block_group);
-		panic ("ext2_free_block: Unable to load group bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "ext2_free_block",
+			    "Unable to load group bitmap\n"
+			    "block_group = %lu", block_group);
 	if (!clear_bit (bit, bh->b_data))
-		printk ("ext2_free_block (%04x:%d): bit already cleared\n",
-			sb->s_dev, block);
+		ext2_warning (sb, "ext2_free_block",
+			      "bit already cleared for block %lu", block);
 	else {
 		group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
 		desc = block_group % EXT2_DESC_PER_BLOCK(sb);
 		bh2 = sb->u.ext2_sb.s_group_desc[group_desc];
-		if (!bh2) {
-			printk ("group_desc = %d\n", group_desc);
-			panic ("ext2_free_block: Group descriptor not loaded");
-		}
+		if (!bh2)
+			ext2_panic (sb, "ext2_free_block",
+				    "Group descriptor not loaded\n"
+				    "group_desc = %lu", group_desc);
 		gdp = (struct ext2_group_desc *) bh2->b_data;
 		gdp[desc].bg_free_blocks_count++;
 		bh2->b_dirt = 1;
+		es->s_free_blocks_count++;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
 	}
 	bh->b_dirt = 1;
-	es->s_free_blocks_count++;
-	sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	if (sb->s_flags & MS_SYNC) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	sb->s_dirt = 1;
 	unlock_super (sb);
 	return;
@@ -307,7 +316,7 @@
 	static int goal_hits = 0, goal_attempts = 0;
 #endif
 	if (!sb) {
-		printk ("ext2_new_block: nonexistant device");
+		printk ("ext2_new_block: nonexistent device");
 		return 0;
 	}
 	lock_super (sb);
@@ -317,9 +326,7 @@
 		return 0;
 	}
 
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_new_block: goal=%d.\n", goal);
-#endif
+	ext2_debug ("goal=%lu.\n", goal);
 	
 repeat:
 	/* First, test whether the goal block is free. */
@@ -329,7 +336,8 @@
 	gdp = (struct ext2_group_desc *) 
 		sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
 	if (!gdp) {
-		panic ("ext2_new_block: Descriptor not loaded");
+		ext2_panic (sb, "ext2_new_block",
+			    "Descriptor not loaded for group %d", i);
 	}
 	if (gdp[desc].bg_free_blocks_count > 0) {
 		j = ((goal - es->s_first_data_block) %
@@ -341,17 +349,19 @@
 		bitmap_nr = load_block_bitmap (sb, i);
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 		if (!bh) {
-			printk ("Cannot load bitmap_nr %d.\n", bitmap_nr);
+			ext2_panic (sb, "ext2_new_block",
+				    "Cannot load bitmap %d", bitmap_nr);
 			unlock_super (sb);
 			return 0;
 		}
-#ifdef EXT2FS_DEBUG
-		printk ("goal is at %d[%d,%d]:%d.\n", i, group_desc, desc, j);
-#endif
+
+		ext2_debug ("goal is at %d[%lu,%lu]:%d.\n", i, group_desc,
+			     desc, j);
+
 		if (!test_bit(j, bh->b_data)) {
 #ifdef EXT2FS_DEBUG
 			goal_hits++;
-			printk ("ext2_new_block: goal bit allocated.\n");
+			ext2_debug ("goal bit allocated.\n");
 #endif
 			goto got_block;
 		}
@@ -377,9 +387,8 @@
 			}
 		}
 	
-#ifdef EXT2FS_DEBUG
-		printk ("Bit not found near goal\n");
-#endif
+		ext2_debug ("Bit not found near goal\n");
+
 		/* There has been no free block found in the near vicinity
 		   of the goal: do a search forward through the block groups,
 		   searching in each group first for an entire free byte in
@@ -404,9 +413,8 @@
 		}
 	}
 
-#ifdef EXT2FS_DEBUG
-	printk ("Bit not found in block group %d.\n", i);
-#endif
+	ext2_debug ("Bit not found in block group %d.\n", i);
+
 	/* Now search the rest of the groups.  We assume that group_desc, desc,
 	   i and gdp correctly point to the last group visited. */
 	for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) {
@@ -429,7 +437,8 @@
 			}
 		}
 		if (!gdp) {
-			panic ("ext2_new_block: Descriptor not loaded");
+			ext2_panic (sb, "ext2_new_block",
+				    "Descriptor not loaded for group %d", i);
 		}
 		if (gdp[desc].bg_free_blocks_count > 0)
 			break;
@@ -440,10 +449,9 @@
 	}
 	bitmap_nr = load_block_bitmap (sb, i);
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
-	if (!bh) {
-		printk ("block_group = %d\n", i);
-		panic ("ext2_new_block: Unable to load group bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "ext2_new_block",
+			    "Unable to load bitmap for group %d", i);
 	r = find_first_zero_byte (bh->b_data, 
 				  EXT2_BLOCKS_PER_GROUP(sb) >> 3);
 	j = (r - bh->b_data) << 3;
@@ -451,36 +459,40 @@
 		j = find_first_zero_bit ((unsigned long *) bh->b_data,
 					 EXT2_BLOCKS_PER_GROUP(sb));
 	if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
-		printk ("ext2_new_block: "
-			"Unable to locate free bit in block group %d.\n",i);
+		ext2_error (sb, "ext2_new_block",
+			    "Unable to locate free bit in block group %d", i);
 		unlock_super (sb);
 		return 0;
 	}
 		
 got_block:
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_new_block: using block group %d(%d,%d,%d)\n", 
-		i, group_desc, desc, gdp[desc].bg_free_blocks_count);
-#endif
+
+	ext2_debug ("using block group %d(%lu,%lu,%d)\n", 
+		    i, group_desc, desc, gdp[desc].bg_free_blocks_count);
 
 	if (set_bit (j, bh->b_data)) {
-		printk ("ext2_new_block: bit already set\n");
+		ext2_warning (sb, "ext2_new_block",
+			      "bit already set for block %d", j);
 		goto repeat;
 	}
 	bh->b_dirt = 1;
-	
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_new_block: found bit %d\n", j);
-#endif
+	if (sb->s_flags & MS_SYNC) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
+
+	ext2_debug ("found bit %d\n", j);
+
 	j += i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
 	if (j >= es->s_blocks_count) {
-		printk ("block_group = %d,block=%d\n", i, j);
-		printk ("ext2_new_block: block >= blocks count");
+		ext2_error (sb, "ext2_new_block",
+			    "block >= blocks count\n"
+			    "block_group = %d, block=%d", i, j);
 		unlock_super (sb);
 		return 0;
 	}
 	if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
-		printk ("ext2_new_block: cannot get block");
+		ext2_error (sb, "ext2_new_block", "cannot get block %d", j);
 		unlock_super (sb);
 		return 0;
 	}
@@ -488,10 +500,10 @@
 	bh->b_uptodate = 1;
 	bh->b_dirt = 1;
 	brelse (bh);
-#ifdef EXT2FS_DEBUG
-	printk("ext2_new_block: allocating block %d. "
-	       "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
-#endif
+
+	ext2_debug ("allocating block %d. "
+		    "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
+
 	gdp[desc].bg_free_blocks_count--;
 	sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1;
 	es->s_free_blocks_count--;
@@ -501,7 +513,7 @@
 	return j;
 }
 
-unsigned long ext2_count_free_blocks (struct super_block *sb)
+unsigned long ext2_count_free_blocks (struct super_block * sb)
 {
 #ifdef EXT2FS_DEBUG
 	struct ext2_super_block * es;
@@ -539,7 +551,7 @@
 			x = 0;
 			printk ("Cannot load bitmap for group %d\n", i);
 		}
-		printk ("group %d: stored = %d, counted = %d\n",
+		printk ("group %d: stored = %d, counted = %lu\n",
 			i, gdp[desc].bg_free_blocks_count, x);
 		bitmap_count += x;
 		desc++;
@@ -549,7 +561,7 @@
 			gdp = NULL;
 		}
 	}
-	printk("ext2_count_free_blocks: stored = %d, computed = %d, %d\n",
+	printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
 	       es->s_free_blocks_count, desc_count, bitmap_count);
 	unlock_super (sb);
 	return bitmap_count;
@@ -557,3 +569,63 @@
 	return sb->u.ext2_sb.s_es->s_free_blocks_count;
 #endif
 }
+
+void ext2_check_blocks_bitmap (struct super_block * sb)
+{
+	struct ext2_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	unsigned long group_desc;
+	unsigned long desc;
+	int bitmap_nr;
+	struct ext2_group_desc * gdp;
+	int i;
+	
+	lock_super (sb);
+	es = sb->u.ext2_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	group_desc = 0;
+	desc = 0;
+	gdp = NULL;
+	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+		if (!gdp) {
+			if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+				ext2_error (sb, "ext2_check_blocks_bitmap",
+					    "Descriptor not loaded for group %d",
+					    i);
+				break;
+			}
+			gdp = (struct ext2_group_desc *) 
+				sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+		}
+		desc_count += gdp[desc].bg_free_blocks_count;
+		bitmap_nr = load_block_bitmap (sb, i);
+		if (sb->u.ext2_sb.s_block_bitmap[bitmap_nr])
+			x = ext2_count_free 
+				(sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
+				 sb->s_blocksize);
+		else {
+			x = 0;
+			ext2_error (sb, "ext2_check_blocks_bitmap",
+				    "Cannot load bitmap for group %d\n", i);
+		}
+		if (gdp[desc].bg_free_blocks_count != x)
+			ext2_error (sb, "ext2_check_blocks_bitmap",
+				    "Wrong free blocks count for group %d, "
+				    "stored = %d, counted = %lu", i,
+				    gdp[desc].bg_free_blocks_count, x);
+		bitmap_count += x;
+		desc++;
+		if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+			group_desc++;
+			desc = 0;
+			gdp = NULL;
+		}
+	}
+	if (es->s_free_blocks_count != bitmap_count)
+		ext2_error (sb, "ext2_check_blocks_bitmap",
+			    "Wrong free blocks count in super block, "
+			    "stored = %lu, counted = %lu",
+			    es->s_free_blocks_count, bitmap_count);
+	unlock_super (sb);
+}
diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c
index 1c69451..70278ae 100644
--- a/fs/ext2/bitmap.c
+++ b/fs/ext2/bitmap.c
@@ -4,17 +4,14 @@
  *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
  */
 
-#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 
-#ifdef EXT2FS_DEBUG
-
 static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
 
-unsigned long ext2_count_free (struct buffer_head * map, unsigned numchars)
+unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
 {
-	unsigned i;
+	unsigned int i;
 	unsigned long sum = 0;
 	
 	if (!map) 
@@ -24,5 +21,3 @@
 			nibblemap[(map->b_data[i] >> 4) & 0xf];
 	return (sum);
 }
-
-#endif
diff --git a/fs/ext2/dcache.c b/fs/ext2/dcache.c
index 4d61691..8c3e69f 100644
--- a/fs/ext2/dcache.c
+++ b/fs/ext2/dcache.c
@@ -19,11 +19,13 @@
 
 #ifndef DONT_USE_DCACHE
 
+#define DCACHE_NAME_LEN	32
+
 struct dir_cache_entry {
 	unsigned short dev;
 	unsigned long dir;
 	unsigned long ino;
-	char name[EXT2_NAME_LEN];
+	char name[DCACHE_NAME_LEN + 1];
 	int len;
 	struct dir_cache_entry * queue_prev;
 	struct dir_cache_entry * queue_next;
@@ -102,7 +104,7 @@
 
 	printk ("%s: cache status\n", func_name);
 	for (p = first; p != NULL; p = p->next)
-		printk ("dev:%04x, dir=%4d, name=%s\n",
+		printk ("dev:%04x, dir=%4lu, name=%s\n",
 			p->dev, p->dir, p->name);
 }
 #endif
@@ -148,6 +150,8 @@
 		p->next->prev = p->prev;
 	else
 		last = p->prev;
+	p->prev = NULL;
+	p->next = NULL;
 }
 
 /*
@@ -163,6 +167,8 @@
 		p->queue_next->queue_prev = p->queue_prev;
 	else
 		queue_tail[queue] = p->queue_prev;
+	p->queue_prev = NULL;
+	p->queue_next = NULL;
 }
 
 /*
@@ -202,12 +208,12 @@
 
 	if (!cache_initialized)
 		init_cache ();
-	if (len > EXT2_NAME_LEN)
-		len = EXT2_NAME_LEN;
+	if (len > DCACHE_NAME_LEN)
+		return 0;
 	memcpy (our_name, (char *) name, len);
 	our_name[len] = '\0';
 #ifdef EXT2FS_DEBUG_CACHE
-	printk ("dcache_lookup (%04x, %d, %s, %d)\n", dev, dir, our_name, len);
+	printk ("dcache_lookup (%04x, %lu, %s, %d)\n", dev, dir, our_name, len);
 #endif
 	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, our_name, len))) {
@@ -221,7 +227,7 @@
 		}
 #ifdef EXT2FS_DEBUG_CACHE
 		hits++;
-		printk ("dcache_lookup: %s,hit,inode=%d,hits=%d,misses=%d\n",
+		printk ("dcache_lookup: %s,hit,inode=%lu,hits=%d,misses=%d\n",
 			our_name, p->ino, hits, misses);
 		show_cache ("dcache_lookup");
 #endif
@@ -244,7 +250,7 @@
  * and the functions which create directory entries
  */
 void ext2_dcache_add (unsigned short dev, unsigned long dir, const char * name,
-		      int len, int ino)
+		      int len, unsigned long ino)
 {
 	struct dir_cache_entry * p;
 	int queue;
@@ -252,11 +258,11 @@
 	if (!cache_initialized)
 		init_cache ();
 #ifdef EXT2FS_DEBUG_CACHE
-	printk ("dcache_add (%04x, %d, %s, %d, %d)\n",
+	printk ("dcache_add (%04x, %lu, %s, %d, %lu)\n",
 		dev, dir, name, len, ino);
 #endif
-	if (len > EXT2_NAME_LEN)
-		len = EXT2_NAME_LEN;
+	if (len > DCACHE_NAME_LEN)
+		return;
 	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, name, len))) {
 		p->dir = dir;
@@ -312,10 +318,10 @@
 	if (!cache_initialized)
 		init_cache ();
 #ifdef EXT2FS_DEBUG_CACHE
-	printk ("dcache_remove (%04x, %d, %s, %d)\n", dev, dir, name, len);
+	printk ("dcache_remove (%04x, %lu, %s, %d)\n", dev, dir, name, len);
 #endif
-	if (len > EXT2_NAME_LEN)
-		len = EXT2_NAME_LEN;
+	if (len > DCACHE_NAME_LEN)
+		return;
 	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, name, len))) {
 		remove_from_cache (p);
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index ff43e3a..24725fb 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -17,8 +17,8 @@
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
-#include <linux/stat.h>
 #include <linux/sched.h>
+#include <linux/stat.h>
 
 #if 0
 static int ext2_dir_read (struct inode * inode, struct file * filp,
@@ -67,7 +67,7 @@
 
 int ext2_check_dir_entry (char * function, struct inode * dir,
 			  struct ext2_dir_entry * de, struct buffer_head * bh,
-			  unsigned int offset)
+			  unsigned long offset)
 {
 	char * error_msg = NULL;
 
@@ -81,19 +81,19 @@
 		 dir->i_sb->s_blocksize)
 		error_msg = "directory entry across blocks";
 
-	if (error_msg != NULL) {
-		printk ("%s: bad directory entry (dev %04x, dir %d): %s\n",
-			function, dir ? dir->i_dev : 0, dir->i_ino, error_msg);
-		printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
-			offset, de->inode, de->rec_len,	de->name_len);
-	}
+	if (error_msg != NULL)
+		ext2_error (dir->i_sb, function, "bad directory entry: %s\n"
+			    "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
+			    error_msg, offset, de->inode, de->rec_len,
+			    de->name_len);
 	return error_msg == NULL ? 1 : 0;
 }
 
 static int ext2_readdir (struct inode * inode, struct file * filp,
 			 struct dirent * dirent, int count)
 {
-	unsigned int offset, i;
+	unsigned long offset;
+	int i;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 	struct super_block * sb;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 463dc00..4dd553d 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -15,11 +15,12 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#include <linux/sched.h>
-#include <linux/ext2_fs.h>
-#include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
 #include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
 
@@ -88,7 +89,8 @@
 	}
 	sb = inode->i_sb;
 	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) {
-		printk ("ext2_file_read: mode = %07o\n", inode->i_mode);
+		ext2_warning (sb, "ext2_file_read", "mode = %07o",
+			      inode->i_mode);
 		return -EINVAL;
 	}
 	offset = filp->f_pos;
@@ -216,7 +218,8 @@
 	}
 	sb = inode->i_sb;
 	if (!S_ISREG(inode->i_mode)) {
-		printk ("ext2_file_write: mode = %07o\n", inode->i_mode);
+		ext2_warning (sb, "ext2_file_write", "mode = %07o\n",
+			      inode->i_mode);
 		return -EINVAL;
 	}
 /*
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index 08513c3..9a11ccc 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -14,14 +14,12 @@
 #include <asm/system.h>
 
 #include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
-#include <linux/locks.h>
-
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
 
 
 #define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 4444048..81053de 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -23,10 +23,11 @@
 
 */
 
-#include <linux/sched.h>
+#include <linux/fs.h>
 #include <linux/ext2_fs.h>
-#include <linux/stat.h>
 #include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/locks.h>
 
@@ -35,6 +36,7 @@
 static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
 {
 	int res;
+
 	if (!size)
 		return 0;
 	__asm__("
@@ -52,7 +54,7 @@
 		shll $3,%%edi
 		addl %%edi,%%edx"
 		: "=d" (res)
-		:"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
+		: "c" ((size + 31) >> 5), "D" (addr), "b" (addr)
 		: "ax", "bx", "cx", "di");
 	return res;
 }
@@ -68,18 +70,17 @@
 
 	group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
 	desc = block_group % EXT2_DESC_PER_BLOCK(sb);
-	if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
-		printk ("block_group = %d,group_desc = %d,desc = %d\n",
-			 block_group, group_desc, desc);
-		panic ("read_inode_bitmap: Group descriptor not loaded");
-	}
+	if (!sb->u.ext2_sb.s_group_desc[group_desc])
+		ext2_panic (sb, "read_inode_bitmap",
+			    "Group descriptor not loaded\n"
+			    "block_group = %lu, group_desc = %lu, desc = %lu",
+			    block_group, group_desc, desc);
 	gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
 	bh = bread (sb->s_dev, gdp[desc].bg_inode_bitmap, sb->s_blocksize);
-	if (!bh) {
-		printk ("block_group = %d,group_desc = %d,desc = %d,inode_bitmap = %d\n",
-			block_group, group_desc, desc, gdp[desc].bg_inode_bitmap);
-		panic ("read_inode_bitmap: Cannot read inode bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "read_inode_bitmap", "Cannot read inode bitmap\n"
+			    "block_group = %lu, group_desc = %lu, desc = %lu, inode_bitmap = %lu",
+			    block_group, group_desc, desc, gdp[desc].bg_inode_bitmap);
 	sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
 }
@@ -102,18 +103,19 @@
 	unsigned long inode_bitmap_number;
 	struct buffer_head * inode_bitmap;
 
-	if (block_group >= sb->u.ext2_sb.s_groups_count) {
-		printk ("block_group = %d, groups_count = %d\n",
-			block_group, sb->u.ext2_sb.s_groups_count);
-		panic ("load_inode_bitmap: block_group >= groups_count");
-	}
+	if (block_group >= sb->u.ext2_sb.s_groups_count)
+		ext2_panic (sb, "load_inode_bitmap",
+			    "block_group >= groups_count\n"
+			    "block_group = %d, groups_count = %lu",
+			     block_group, sb->u.ext2_sb.s_groups_count);
 	if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 &&
 	    sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group)
 		return 0;
 	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
 		if (sb->u.ext2_sb.s_inode_bitmap[block_group]) {
 			if (sb->u.ext2_sb.s_inode_bitmap_number[block_group] != block_group)
-				panic ("load_inode_bitmap: block_group != inode_bitmap_number");
+				ext2_panic (sb, "load_inode_bitmap",
+					    "block_group != inode_bitmap_number");
 			else
 				return block_group;
 		} else {
@@ -160,7 +162,7 @@
  * This may be used one day by an 'undelete' program
  */
 static void set_inode_dtime (struct inode * inode,
-			     struct ext2_group_desc *gdp, unsigned long desc)
+			     struct ext2_group_desc * gdp, unsigned long desc)
 {
 	unsigned long inode_block;
 	struct buffer_head * bh;
@@ -170,10 +172,11 @@
 			EXT2_INODES_PER_GROUP(inode->i_sb)) /
 			EXT2_INODES_PER_BLOCK(inode->i_sb));
 	bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
-	if (!bh) {
-		printk ("inode=%d, inode_block=%d\n", inode->i_ino, inode_block);
-		panic ("set_inode_dtime: Cannot load inode table block");
-	}
+	if (!bh)
+		ext2_panic (inode->i_sb, "set_inode_dtime",
+			    "Cannot load inode table block\n"
+			    "inode=%lu, inode_block=%lu",
+			    inode->i_ino, inode_block);
 	raw_inode = ((struct ext2_inode *) bh->b_data) +
 			(((inode->i_ino - 1) %
 			EXT2_INODES_PER_GROUP(inode->i_sb)) %
@@ -181,6 +184,10 @@
 	raw_inode->i_links_count = 0;
 	raw_inode->i_dtime = CURRENT_TIME;
 	bh->b_dirt = 1;
+	if (IS_SYNC(inode)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	brelse (bh);
 }
 
@@ -217,14 +224,15 @@
 		printk("ext2_free_inode: inode on nonexistent device\n");
 		return;
 	}
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_free_inode: freeing inode %d\n", inode->i_ino);
-#endif
+
+	ext2_debug ("freeing inode %lu\n", inode->i_ino);
+
 	sb = inode->i_sb;
 	lock_super (sb);
-	if (inode->i_ino < 1 ||
+	if (inode->i_ino < EXT2_FIRST_INO ||
 	    inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
-		printk("free_inode: inode 0 or nonexistent inode\n");
+		ext2_error (sb, "free_inode",
+			    "reserved inode or nonexistent inode");
 		unlock_super (sb);
 		return;
 	}
@@ -233,31 +241,35 @@
 	bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
 	bitmap_nr = load_inode_bitmap (sb, block_group);
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
-	if (!bh) {
-		printk ("block_group = %d\n", block_group);
-		panic ("ext2_free_inode: Unable to load bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "ext2_free_inode",
+			    "Unable to load bitmap for group %lu", block_group);
 	if (!clear_bit (bit, bh->b_data))
-		printk ("ext2_free_inode (%04x:%d): bit already cleared\n",
-			sb->s_dev, inode->i_ino);
+		ext2_warning (sb, "ext2_free_inode",
+			      "bit already cleared for inode %lu", inode->i_ino);
 	else {
 		group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
 		desc = block_group % EXT2_DESC_PER_BLOCK(sb);
 		bh2 = sb->u.ext2_sb.s_group_desc[group_desc];
-		if (!bh2) {
-			printk ("group_desc = %d\n", group_desc);
-			panic ("ext2_free_inode: Group descriptor not loaded");
-		}
+		if (!bh2)
+			ext2_panic (sb, "ext2_free_inode",
+				    "Group descriptor not loaded for group %lu",
+				    group_desc);
 		gdp = (struct ext2_group_desc *) bh2->b_data;
 		gdp[desc].bg_free_inodes_count++;
 		if (S_ISDIR(inode->i_mode))
 			gdp[desc].bg_used_dirs_count--;
 		bh2->b_dirt = 1;
+		es->s_free_inodes_count++;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
 		set_inode_dtime (inode, gdp, desc);
 	}
 	bh->b_dirt = 1;
-	es->s_free_inodes_count++;
-	sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	if (sb->s_flags & MS_SYNC) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
+
 	sb->s_dirt = 1;
 	clear_inode (inode);
 	unlock_super (sb);
@@ -281,9 +293,10 @@
 			EXT2_INODES_PER_BLOCK(inode->i_sb));
 	bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
 	if (!bh) {
-		printk ("inode=%d, inode_block=%d\n",
-			inode->i_ino, inode_block);
-		printk ("inc_inode_version: Cannot load inode table block");
+		ext2_error (inode->i_sb, "inc_inode_version",
+			    "Cannot load inode table block"
+			    "inode=%lu, inode_block=%lu\n",
+			    inode->i_ino, inode_block);
 		inode->u.ext2_i.i_version = 1;
 		return;
 	}
@@ -303,9 +316,10 @@
 	struct ext2_group_desc * gdp;
 
 	if (group >= sb->u.ext2_sb.s_groups_count || group < 0 )
-		panic ("ext2: get_group_desc: Invalid group\n");
+		ext2_panic (sb, "get_group_desc", "Invalid group %d", group);
 	if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)])
-		panic ("ext2: get_group_desc: Descriptor not loaded");
+		ext2_panic (sb, "get_group_desc",
+			    "Descriptor not loaded for group %d", group);
 	gdp = (struct ext2_group_desc *)
 		sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)]
 		->b_data;
@@ -414,24 +428,29 @@
 	}
 	bitmap_nr = load_inode_bitmap (sb, i);
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
-	if (!bh) {
-		printk ("block_group = %d\n", i);
-		panic ("ext2_new_inode: Unable to load group inode bitmap");
-	}
+	if (!bh)
+		ext2_panic (sb, "ext2_new_inode",
+			    "Unable to load bitmap for group %d", i);
 	if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
 				      EXT2_INODES_PER_GROUP(sb))) <
 	    EXT2_INODES_PER_GROUP(sb)) {
 		if (set_bit (j, bh->b_data)) {
-			printk ("ext2_new_inode: bit already set\n");
+			ext2_warning (sb, "ext2_new_inode",
+				      "bit already set for inode %d", j);
 			goto repeat;
 		}
 		bh->b_dirt = 1;
+		if (sb->s_flags & MS_SYNC) {
+			ll_rw_block (WRITE, 1, &bh);
+			wait_on_buffer (bh);
+		}
 	} else
 		goto repeat;
 	j += i * EXT2_INODES_PER_GROUP(sb) + 1;
 	if (j > es->s_inodes_count) {
-		printk ("block_group = %d,inode=%d\n", i, j);
-		printk ("ext2_new_inode: inode > inodes count");
+		ext2_error (sb, "ext2_new_inode",
+			    "inode > inodes count\n"
+			    "block_group = %d,inode=%d", i, j);
 		unlock_super (sb);
 		iput (inode);
 		return NULL;
@@ -455,7 +474,7 @@
 	inode->i_blksize = sb->s_blocksize;
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	inode->u.ext2_i.i_flags = 0;
+	inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
 	inode->u.ext2_i.i_faddr = 0;
 	inode->u.ext2_i.i_frag = 0;
 	inode->u.ext2_i.i_fsize = 0;
@@ -464,16 +483,18 @@
 	inode->u.ext2_i.i_dtime = 0;
 	inode->u.ext2_i.i_block_group = i;
 	inode->i_op = NULL;
+	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
+		inode->i_flags |= MS_SYNC;
 	insert_inode_hash(inode);
 	inc_inode_version (inode, gdp, mode);
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_new_inode : allocating inode %d\n", inode->i_ino);
-#endif
+
+	ext2_debug ("allocating inode %lu\n", inode->i_ino);
+
 	unlock_super (sb);
 	return inode;
 }
 
-unsigned long ext2_count_free_inodes (struct super_block *sb)
+unsigned long ext2_count_free_inodes (struct super_block * sb)
 {
 #ifdef EXT2FS_DEBUG
 	struct ext2_super_block * es;
@@ -509,7 +530,7 @@
 			printk ("Cannot load inode bitmap for group %d (bitmap = %d)\n",
 				i, bitmap_nr);
 		}
-		printk ("group %d: stored = %d, counted = %d\n",
+		printk ("group %d: stored = %d, counted = %lu\n",
 			i, gdp[desc].bg_free_inodes_count, x);
 		bitmap_count += x;
 		desc++;
@@ -519,7 +540,7 @@
 			gdp = NULL;
 		}
 	}
-	printk("ext2_count_free_inodes: stored = %d, computed = %d, %d\n",
+	printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
 		es->s_free_inodes_count, desc_count, bitmap_count);
 	unlock_super (sb);
 	return desc_count;
@@ -527,3 +548,62 @@
 	return sb->u.ext2_sb.s_es->s_free_inodes_count;
 #endif
 }
+
+void ext2_check_inodes_bitmap (struct super_block * sb)
+{
+	struct ext2_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	unsigned long group_desc;
+	unsigned long desc;
+	int bitmap_nr;
+	struct ext2_group_desc * gdp;
+	int i;
+
+	lock_super (sb);
+	es = sb->u.ext2_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	group_desc = 0;
+	desc = 0;
+	gdp = NULL;
+	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+		if (!gdp) {
+			if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+				ext2_error (sb, "ext2_check_inodes_bitmap",
+					    "Descriptor not loaded for group %d",
+					    i);
+				break;
+			}
+			gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+		}
+		desc_count += gdp[desc].bg_free_inodes_count;
+		bitmap_nr = load_inode_bitmap (sb, i);
+		if (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr])
+			x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
+					       EXT2_INODES_PER_GROUP(sb) / 8);
+		else {
+			x = 0;
+			ext2_error (sb, "ext2_check_inodes_bitmap",
+				    "Cannot load bitmap for group %d (bitmap = %d)",
+				    i, bitmap_nr);
+		}
+		if (gdp[desc].bg_free_inodes_count != x)
+			ext2_error (sb, "ext2_check_inodes_bitmap",
+				    "Wrong free inodes count in group %d, "
+				    "stored = %d, counted = %lu", i,
+				    gdp[desc].bg_free_inodes_count, x);
+		bitmap_count += x;
+		desc++;
+		if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+			group_desc++;
+			desc = 0;
+			gdp = NULL;
+		}
+	}
+	if (es->s_free_inodes_count != bitmap_count)
+		ext2_error (sb, "ext2_check_inodes_bitmap",
+			    "Wrong free inodes count in super block, "
+			    "stored = %lu, counted = %lu",
+			    es->s_free_inodes_count, bitmap_count);
+	unlock_super (sb);
+}
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 65f666f..92c8bdf 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -12,17 +12,17 @@
  *  Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
  */
 
-#include <linux/sched.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
+#include <linux/sched.h>
 #include <linux/stat.h>
+#include <linux/string.h>
 #include <linux/locks.h>
-#include <linux/errno.h>
-
-#include <asm/system.h>
-#include <asm/segment.h>
 
 void ext2_put_inode (struct inode * inode)
 {
@@ -35,388 +35,6 @@
 	ext2_free_inode (inode);
 }
 
-void ext2_put_super (struct super_block * sb)
-{
-	int i;
-
-	lock_super (sb);
-	if ((sb->s_flags & MS_RDONLY) == 0) {
-		sb->u.ext2_sb.s_es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
-		sb->u.ext2_sb.s_sbh->b_dirt = 1;
-	}
-#ifndef DONT_USE_DCACHE
-	ext2_dcache_invalidate (sb->s_dev);
-#endif
-	sb->s_dev = 0;
-	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
-		if (sb->u.ext2_sb.s_group_desc[i])
-			brelse (sb->u.ext2_sb.s_group_desc[i]);
-	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
-		if (sb->u.ext2_sb.s_inode_bitmap[i])
-			brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
-	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
-		if (sb->u.ext2_sb.s_block_bitmap[i])
-			brelse (sb->u.ext2_sb.s_block_bitmap[i]);
-	brelse (sb->u.ext2_sb.s_sbh);
-	unlock_super (sb);
-	return;
-}
-
-static struct super_operations ext2_sops = { 
-	ext2_read_inode,
-	NULL,
-	ext2_write_inode,
-	ext2_put_inode,
-	ext2_put_super,
-	ext2_write_super,
-	ext2_statfs,
-	ext2_remount
-};
-
-#ifdef EXT2FS_PRE_02B_COMPAT
-
-static int convert_pre_02b_fs (struct super_block * sb,
-			       struct buffer_head * bh)
-{
-	struct ext2_super_block * es;
-	struct ext2_old_group_desc old_group_desc [BLOCK_SIZE / sizeof (struct ext2_old_group_desc)];
-	struct ext2_group_desc * gdp;
-	struct buffer_head * bh2;
-	int groups_count;
-	int i;
-
-	es = (struct ext2_super_block *) bh->b_data;
-	bh2 = bread (sb->s_dev, 2, BLOCK_SIZE);
-	if (!bh2) {
-		printk ("Cannot read descriptor blocks while converting !\n");
-		return 0;
-	}
-	memcpy (old_group_desc, bh2->b_data, BLOCK_SIZE);
-	groups_count = (sb->u.ext2_sb.s_blocks_count - 
-			sb->u.ext2_sb.s_first_data_block +
-			(EXT2_BLOCK_SIZE(sb) * 8) - 1) /
-				(EXT2_BLOCK_SIZE(sb) * 8);
-	memset (bh2->b_data, 0, BLOCK_SIZE);
-	gdp = (struct ext2_group_desc *) bh2->b_data;
-	for (i = 0; i < groups_count; i++) {
-		gdp[i].bg_block_bitmap = old_group_desc[i].bg_block_bitmap;
-		gdp[i].bg_inode_bitmap = old_group_desc[i].bg_inode_bitmap;
-		gdp[i].bg_inode_table = old_group_desc[i].bg_inode_table;
-		gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count;
-		gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count;
-	}
-	bh2->b_dirt = 1;
-	brelse (bh2);
-	es->s_magic = EXT2_SUPER_MAGIC;
-	bh->b_dirt = 1;
-	sb->s_magic = EXT2_SUPER_MAGIC;
-	return 1;
-}
-
-#endif
-
-struct super_block * ext2_read_super (struct super_block * s, void * data,
-				      int silent)
-{
-	struct buffer_head * bh;
-	struct ext2_super_block * es;
-	int dev = s->s_dev;
-	int bh_count;
-	int i, j;
-#ifdef EXT2FS_PRE_02B_COMPAT
-	int fs_converted = 0;
-#endif
-
-	lock_super (s);
-	set_blocksize (dev, BLOCK_SIZE);
-	if (!(bh = bread (dev, 1, BLOCK_SIZE))) {
-		s->s_dev = 0;
-		unlock_super (s);
-		printk ("EXT2-fs: unable to read superblock\n");
-		return NULL;
-	}
-	es = (struct ext2_super_block *) bh->b_data;
-	/* Note: s_es must be initialized s_es as soon as possible because
-	   some ext2 macro-instructions depend on its value */
-	s->u.ext2_sb.s_es = es;
-	s->s_magic = es->s_magic;
-	if (s->s_magic != EXT2_SUPER_MAGIC
-#ifdef EXT2FS_PRE_02B_COMPAT
-	   && s->s_magic != EXT2_OLD_SUPER_MAGIC
-#endif
-	   ) {
-		s->s_dev = 0;
-		unlock_super (s);
-		brelse (bh);
-		if (!silent)
-			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
-				dev);
-		return NULL;
-	}
-	s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
-	s->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(s);
-	if (s->s_blocksize != BLOCK_SIZE && 
-	    (s->s_blocksize == 1024 || s->s_blocksize == 2048 ||  
-	     s->s_blocksize == 4096)) {
-		brelse (bh);
-		set_blocksize (dev, s->s_blocksize);
-		bh = bread (dev, 0, s->s_blocksize);
-		if(!bh)
-			return NULL;
-		es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE);
-		s->u.ext2_sb.s_es = es;
-		if (es->s_magic != EXT2_SUPER_MAGIC) {
-			s->s_dev = 0;
-			unlock_super (s);
-			brelse (bh);
-			printk ("ext2-FS: Magic mismatch, very weird !\n");
-			return NULL;
-		}
-	}
-	s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
-				   es->s_log_frag_size;
-	if (s->u.ext2_sb.s_frag_size)
-		s->u.ext2_sb.s_frags_per_block = s->s_blocksize /
-						   s->u.ext2_sb.s_frag_size;
-	else
-		s->s_magic = 0;
-	s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
-	s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
-	s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
-	s->u.ext2_sb.s_inodes_per_block = s->s_blocksize /
-					  sizeof (struct ext2_inode);
-	s->u.ext2_sb.s_desc_per_block = s->s_blocksize /
-					sizeof (struct ext2_group_desc);
-	s->u.ext2_sb.s_sbh = bh;
-	s->u.ext2_sb.s_es = es;
-	s->u.ext2_sb.s_was_mounted_valid = es->s_valid;
-	s->u.ext2_sb.s_rename_lock = 0;
-	s->u.ext2_sb.s_rename_wait = NULL;
-#ifdef EXT2FS_PRE_02B_COMPAT
-	if (s->s_magic == EXT2_OLD_SUPER_MAGIC) {
-		if (es->s_blocks_count > 262144) {
-			 /* fs > 256 MB can't be converted */ 
-			s->s_dev = 0;
-			unlock_super (s);
-			brelse (bh);
-			printk ("EXT2-fs: trying to mount a pre-0.2b file"
-				"system which cannot be converted\n");
-			return NULL;
-		}
-		printk ("EXT2-fs: mounting a pre 0.2b file system, "
-			"will try to convert the structure\n");
-		if (s->s_flags & MS_RDONLY == 0) {
-			s->s_dev = 0;
-			unlock_super (s);
-			brelse (bh);
-			printk ("EXT2-fs: cannot convert a read-only fs\n");
-			return NULL;
-		}
-		if (!convert_pre_02b_fs (s, bh)) {
-			s->s_dev = 0;
-			unlock_super (s);
-			brelse (bh);
-			printk ("EXT2-fs: conversion failed !!!\n");
-			return NULL;
-		}
-		printk ("EXT2-fs: conversion succeeded !!!\n");
-		fs_converted = 1;
-	}
-#endif
-	if (s->s_magic != EXT2_SUPER_MAGIC) {
-		s->s_dev = 0;
-		unlock_super (s);
-		brelse (bh);
-		if (!silent)
-			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
-				dev);
-		return NULL;
-	}
-	if (s->s_blocksize != bh->b_size) {
-		s->s_dev = 0;
-		unlock_super (s);
-		brelse (bh);
-		if (!silent)
-			printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
-				dev);
-		return NULL;
-	}
-
-	if (s->s_blocksize != s->u.ext2_sb.s_frag_size) {
-		s->s_dev = 0;
-		unlock_super (s);
-		brelse (bh);
-		printk ("EXT2-fs: fragsize %d != blocksize %d (not supported yet)\n",
-			s->u.ext2_sb.s_frag_size, s->s_blocksize);
-		return NULL;
-	}
-
-	if (!es->s_valid)
-		printk ("EXT2-fs warning: mounting unchecked file system, "
-			"running e2fsck is recommended\n");
-	s->u.ext2_sb.s_groups_count = (es->s_blocks_count -
-				       es->s_first_data_block +
-				       EXT2_BLOCKS_PER_GROUP(s) - 1) /
-				       EXT2_BLOCKS_PER_GROUP(s);
-	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
-		s->u.ext2_sb.s_group_desc[i] = NULL;
-	bh_count = (s->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(s) - 1) /
-		   EXT2_DESC_PER_BLOCK(s);
-	if (bh_count >= EXT2_MAX_GROUP_DESC) {
-		s->s_dev = 0;
-		unlock_super (s);
-		brelse (bh);
-		printk ("EXT2-fs: file system too big\n");
-		return NULL;
-	}
-	for (i = 0; i < bh_count; i++) {
-		s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2,
-						      s->s_blocksize);
-		if (!s->u.ext2_sb.s_group_desc[i]) {
-			s->s_dev = 0;
-			unlock_super (s);
-			for (j = 0; j < i; j++)
-				brelse (s->u.ext2_sb.s_group_desc[i]);
-			brelse (bh);
-			printk ("ext2-FS: unable to read group descriptors\n");
-			return NULL;
-		}
-	}
-	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
-		s->u.ext2_sb.s_inode_bitmap_number[i] = 0;
-		s->u.ext2_sb.s_inode_bitmap[i] = NULL;
-		s->u.ext2_sb.s_block_bitmap_number[i] = 0;
-		s->u.ext2_sb.s_block_bitmap[i] = NULL;
-	}
-	s->u.ext2_sb.s_loaded_inode_bitmaps = 0;
-	s->u.ext2_sb.s_loaded_block_bitmaps = 0;
-	unlock_super (s);
-	/* set up enough so that it can read an inode */
-	s->s_dev = dev;
-	s->s_op = &ext2_sops;
-	if (!(s->s_mounted = iget (s, EXT2_ROOT_INO))) {
-		s->s_dev = 0;
-		for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
-			if (s->u.ext2_sb.s_group_desc[i])
-				brelse (s->u.ext2_sb.s_group_desc[i]);
-		brelse (bh);
-		printk ("EXT2-fs: get root inode failed\n");
-		return NULL;
-	}
-	if ((s->s_flags & MS_RDONLY) == 0) {
-		es->s_valid = 0;
-		es->s_mtime = CURRENT_TIME;
-		bh->b_dirt = 1;
-		s->s_dirt = 1;
-	}
-#ifdef EXT2FS_PRE_02B_COMPAT
-	if (fs_converted) {
-		for (i = 0; i < bh_count; i++)
-			s->u.ext2_sb.s_group_desc[i]->b_dirt = 1;
-		s->s_dirt = 1;
-	}
-#endif
-	printk ("[EXT II FS %s, %s, bs=%d, fs=%d, gc=%d, bpg=%d, ipg=%d]\n",
-		EXT2FS_VERSION, EXT2FS_DATE, s->s_blocksize,
-		s->u.ext2_sb.s_frag_size, s->u.ext2_sb.s_groups_count,
-		EXT2_BLOCKS_PER_GROUP(s), EXT2_INODES_PER_GROUP(s));
-	return s;
-}
-
-/*
- * In the second extended file system, it is not necessary to
- * write the super block since we use a mapping of the
- * disk super block in a buffer.
- *
- * However, this function is still used to set the fs valid
- * flags to 0.  We need to set this flag to 0 since the fs
- * may have been checked while mounted and e2fsck may have
- * set s_valid to 1 after some corrections.
- */
-
-static void ext2_commit_super (struct super_block * sb,
-			       struct ext2_super_block * es)
-{
-	es->s_wtime = CURRENT_TIME;
-	sb->u.ext2_sb.s_sbh->b_dirt = 1;
-	sb->s_dirt = 0;
-}
-
-void ext2_write_super (struct super_block * sb)
-{
-	struct ext2_super_block * es;
-
-	if ((sb->s_flags & MS_RDONLY) == 0) {
-		es = sb->u.ext2_sb.s_es;
-#ifdef EXT2FS_DEBUG
-		printk ("ext2_write_super: setting valid to 0\n");
-#endif
-		if (es->s_valid) {
-			es->s_valid = 0;
-			es->s_mtime = CURRENT_TIME;
-		}
-		ext2_commit_super (sb, es);
-	}
-	sb->s_dirt = 0;
-}
-
-int ext2_remount (struct super_block * sb, int * flags)
-{
-	struct ext2_super_block * es;
-
-	es = sb->u.ext2_sb.s_es;
-	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
-		return 0;
-	if (*flags & MS_RDONLY) {
-		if (es->s_valid || !sb->u.ext2_sb.s_was_mounted_valid)
-			return 0;
-		/* OK, we are remounting a valid rw partition rdonly, so set
-		   the rdonly flag and then mark the partition as valid
-		   again. */
-		es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
-		es->s_mtime = CURRENT_TIME;
-		sb->u.ext2_sb.s_sbh->b_dirt = 1;
-		sb->s_dirt = 1;
-		ext2_commit_super (sb, es);
-	}
-	else {
-		/* Mounting a RDONLY partition read-write, so reread and
-		   store the current valid flag.  (It may have been changed 
-		   by e2fsck since we originally mounted the partition.)  */
-		sb->u.ext2_sb.s_was_mounted_valid = es->s_valid;
-		if (!es->s_valid)
-			printk ("EXT2-fs warning: remounting unchecked fs, "
-				"running e2fsck is recommended\n");
-		else {
-			es->s_valid = 0;
-			es->s_mtime = CURRENT_TIME;
-			sb->u.ext2_sb.s_sbh->b_dirt = 1;
-			sb->s_dirt = 1;
-		}
-	}
-	return 0;
-}
-
-void ext2_statfs (struct super_block * sb, struct statfs * buf)
-{
-	long tmp;
-
-	put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type);
-	put_fs_long (sb->s_blocksize, &buf->f_bsize);
-	put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count, &buf->f_blocks);
-	tmp = ext2_count_free_blocks (sb);
-	put_fs_long (tmp, &buf->f_bfree);
-	if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count)
-		put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count,
-			     &buf->f_bavail);
-	else
-		put_fs_long (0, &buf->f_bavail);
-	put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files);
-	put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree);
-	put_fs_long (EXT2_NAME_LEN, &buf->f_namelen);
-	/* Don't know what value to put in buf->f_fsid */
-}
-
 #define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
 
 static int block_bmap (struct buffer_head * bh, int nr)
@@ -436,13 +54,13 @@
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 
 	if (block < 0) {
-		printk("ext2_bmap: block < 0");
+		ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
 		return 0;
 	}
 	if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
 		     addr_per_block * addr_per_block +
 		     addr_per_block * addr_per_block * addr_per_block) {
-		printk ("ext2_bmap: block > big");
+		ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
 		return 0;
 	}
 	if (block < EXT2_NDIR_BLOCKS)
@@ -486,7 +104,7 @@
 }
 
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
-					  int create, int new_block, int *err)
+					  int create, int new_block, int * err)
 {
 	int tmp, goal = 0;
 	unsigned long * p;
@@ -511,9 +129,9 @@
 	}
 	if (inode->u.ext2_i.i_next_alloc_block == new_block)
 		goal = inode->u.ext2_i.i_next_alloc_goal;
-#ifdef EXT2FS_DEBUG
-	printk ("ext2 inode_getblk: hint = %d,", goal);
-#endif
+
+	ext2_debug ("hint = %d,", goal);
+
 	if (!goal) {
 		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (inode->u.ext2_i.i_data[tmp]) {
@@ -527,9 +145,8 @@
 			       inode->i_sb->u.ext2_sb.s_es->s_first_data_block;
 	}
 
-#ifdef EXT2FS_DEBUG
-	printk (" goal = %d.\n", goal);
-#endif
+	ext2_debug ("goal = %d.\n", goal);
+
 	tmp = ext2_new_block (inode->i_sb, goal);
 	if (!tmp)
 		return NULL;
@@ -544,14 +161,17 @@
 	inode->u.ext2_i.i_next_alloc_goal = tmp;
 	inode->i_ctime = CURRENT_TIME;
 	inode->i_blocks += blocks;
-	inode->i_dirt = 1;
+	if (IS_SYNC(inode))
+		ext2_sync_inode (inode);
+	else
+		inode->i_dirt = 1;
 	return result;
 }
 
 static struct buffer_head * block_getblk (struct inode * inode,
 					  struct buffer_head * bh, int nr,
 					  int create, int blocksize, 
-					  int new_block, int *err)
+					  int new_block, int * err)
 {
 	int tmp, goal = 0;
 	unsigned long * p;
@@ -568,7 +188,7 @@
 			return NULL;
 		}
 	}
-	p = nr + (unsigned long *) bh->b_data;
+	p = (unsigned long *) bh->b_data + nr;
 repeat:
 	tmp = *p;
 	if (tmp) {
@@ -597,13 +217,10 @@
 			}
 		}
 		if (!goal)
-			goal = bh->b_blocknr+1;
+			goal = bh->b_blocknr + 1;
 	}
 	tmp = ext2_new_block (inode->i_sb, goal);
 	if (!tmp) {
-#ifdef EXT2FS_DEBUG
-		printk ("inode_getblk: ext2_new_block returned 0\n");
-#endif
 		brelse (bh);
 		return NULL;
 	}
@@ -615,6 +232,10 @@
 	}
 	*p = tmp;
 	bh->b_dirt = 1;
+	if (IS_SYNC(inode)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	inode->i_ctime = CURRENT_TIME;
 	inode->i_blocks += blocks;
 	inode->i_dirt = 1;
@@ -624,32 +245,32 @@
 	return result;
 }
 
-struct buffer_head * ext2_getblk (struct inode * inode, int block,
-				  int create, int *err)
+struct buffer_head * ext2_getblk (struct inode * inode, long block,
+				  int create, int * err)
 {
 	struct buffer_head * bh;
-	int b;
+	unsigned long b;
 	unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 
 	*err = -EIO;
 	if (block < 0) {
-		printk ("ext2_getblk: block < 0\n");
+		ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
 		return NULL;
 	}
 	if (block > EXT2_NDIR_BLOCKS + addr_per_block  +
 		    addr_per_block * addr_per_block +
 		    addr_per_block * addr_per_block * addr_per_block) {
-		printk ("ext2_getblk: block > big\n");
+		ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
 		return NULL;
 	}
 	/* If this is a sequential block allocation, set the next_alloc_block
 	   to this block now so that all the indblock and data block
 	   allocations use the same goal zone */
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_getblk: block %d, next %d, goal %d.\n", block, 
-		inode->u.ext2_i.i_next_alloc_block,
-		inode->u.ext2_i.i_next_alloc_goal);
-#endif
+
+	ext2_debug ("block %lu, next %lu, goal %lu.\n", block, 
+		    inode->u.ext2_i.i_next_alloc_block,
+		    inode->u.ext2_i.i_next_alloc_goal);
+
 	if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
 		inode->u.ext2_i.i_next_alloc_block++;
 		inode->u.ext2_i.i_next_alloc_goal++;
@@ -713,24 +334,28 @@
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
 	     inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
 	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
-		printk ("ext2_read_inode: bad inode number on dev %0x04: %d\n",
-			inode->i_dev, inode->i_ino);
+		ext2_error (inode->i_sb, "ext2_read_inode",
+			    "bad inode number: %lu", inode->i_ino);
 		return;
 	}
 	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
 	if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
-		panic ("ext2_read_inode: group >= groups count");
+		ext2_panic (inode->i_sb, "ext2_read_inode",
+			    "group >= groups count");
 	group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
 	desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
 	bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
 	if (!bh)
-		panic ("ext2_read_inode: Descriptor not loaded");
+		ext2_panic (inode->i_sb, "ext2_read_inode",
+			    "Descriptor not loaded");
 	gdp = (struct ext2_group_desc *) bh->b_data;
 	block = gdp[desc].bg_inode_table +
 		(((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
 		 / EXT2_INODES_PER_BLOCK(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
-		panic ("ext2_read_inode: unable to read i-node block");
+		ext2_panic (inode->i_sb, "ext2_read_inode",
+			    "unable to read i-node block\n"
+			    "inode=%lu, block=%lu", inode->i_ino, block);
 	raw_inode = ((struct ext2_inode *) bh->b_data) +
 		(inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
 	inode->i_mode = raw_inode->i_mode;
@@ -775,6 +400,8 @@
 		inode->i_op = &blkdev_inode_operations;
 	else if (S_ISFIFO(inode->i_mode))
 		init_fifo(inode);
+	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
+		inode->i_flags |= MS_SYNC;
 }
 
 static struct buffer_head * ext2_update_inode (struct inode * inode)
@@ -789,24 +416,28 @@
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
 	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
-		printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n",
-			inode->i_dev, inode->i_ino);
+		ext2_error (inode->i_sb, "ext2_write_inode",
+			    "bad inode number: %lu", inode->i_ino);
 		return 0;
 	}
 	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
 	if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
-		panic ("ext2_write_inode: group >= groups count");
+		ext2_panic (inode->i_sb, "ext2_write_inode",
+			    "group >= groups count");
 	group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
 	desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
 	bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
 	if (!bh)
-		panic ("ext2_write_inode: Descriptor not loaded");
+		ext2_panic (inode->i_sb, "ext2_write_inode",
+			    "Descriptor not loaded");
 	gdp = (struct ext2_group_desc *) bh->b_data;
 	block = gdp[desc].bg_inode_table +
 		(((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
 		 / EXT2_INODES_PER_BLOCK(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
-		panic ("ext2_write_inode: unable to read i-node block");
+		ext2_panic (inode->i_sb, "ext2_write_inode",
+			    "unable to read i-node block\n"
+			    "inode=%lu, block=%lu", inode->i_ino, block);
 	raw_inode = ((struct ext2_inode *)bh->b_data) +
 		(inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
 	raw_inode->i_mode = inode->i_mode;
@@ -854,7 +485,7 @@
 		wait_on_buffer (bh);
 		if (bh->b_req && !bh->b_uptodate)
 		{
-			printk ("IO error syncing ext2 inode [%04x:%08x]\n",
+			printk ("IO error syncing ext2 inode [%04x:%08lx]\n",
 				inode->i_dev, inode->i_ino);
 			err = -1;
 		}
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 4262172..d01f9ef 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -4,15 +4,46 @@
  * Copyright (C) 1993  Remy Card (card@masi.ibp.fr)
  */
 
-#include <linux/sched.h>
+#include <asm/segment.h>
+
 #include <linux/errno.h>
+#include <linux/fs.h>
 #include <linux/ext2_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
 
 int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 		unsigned long arg)
 {
-#ifdef EXT2FS_DEBUG
-	printk ("ext2_ioctl: cmd = %d, arg = %d\n", cmd, arg);
-#endif
-	return -EINVAL;
+
+	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
+
+	switch (cmd) {
+	case EXT2_IOC_GETFLAGS:
+		put_fs_long (inode->u.ext2_i.i_flags, (long *) arg);
+		return 0;
+	case EXT2_IOC_SETFLAGS:
+		if ((current->euid != inode->i_uid) && !suser())
+			return -EPERM;
+		if (IS_RDONLY(inode))
+			return -EROFS;
+		inode->u.ext2_i.i_flags = get_fs_long ((long *) arg);
+		inode->i_ctime = CURRENT_TIME;
+		inode->i_dirt = 1;
+		return 0;
+	case EXT2_IOC_GETVERSION:
+		put_fs_long (inode->u.ext2_i.i_version, (long *) arg);
+		return 0;
+	case EXT2_IOC_SETVERSION:
+		if ((current->euid != inode->i_uid) && !suser())
+			return -EPERM;
+		if (IS_RDONLY(inode))
+			return -EROFS;
+		inode->u.ext2_i.i_version = get_fs_long ((long *) arg);
+		inode->i_ctime = CURRENT_TIME;
+		inode->i_dirt = 1;
+		return 0;
+	default:
+		return -EINVAL;
+	}
 }
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index c387807..ff04968 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -10,16 +10,18 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
-#include <linux/sched.h>
-#include <linux/ext2_fs.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
-#include <linux/errno.h>
-
 #include <asm/segment.h>
 
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
 /*
  * comment out this line if you want names > EXT2_NAME_LEN chars to be
  * truncated. Else they will be disallowed.
@@ -64,7 +66,7 @@
 					     const char * const name, int namelen,
 					     struct ext2_dir_entry ** res_dir)
 {
-	long offset;
+	unsigned long offset;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 	struct super_block * sb;
@@ -101,7 +103,7 @@
 			brelse (bh);
 			return NULL;
 		}
-		if (ext2_match (namelen, name, de)) {
+		if (de->inode != 0 && ext2_match (namelen, name, de)) {
 			*res_dir = de;
 			return bh;
 		}
@@ -115,7 +117,7 @@
 int ext2_lookup (struct inode * dir, const char * name, int len,
 		 struct inode ** result)
 {
-	int ino;
+	unsigned long ino;
 	struct ext2_dir_entry * de;
 	struct buffer_head * bh;
 
@@ -165,8 +167,7 @@
 					    struct ext2_dir_entry ** res_dir,
 					    int *err)
 {
-	int i;
-	long offset;
+	unsigned long offset;
 	unsigned short rec_len;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de, * de1;
@@ -212,19 +213,21 @@
 					*err = -ENOENT;
 					return NULL;
 				}
-#ifdef EXT2FS_DEBUG
-				printk ("ext2_add_entry: creating next block\n");
-#endif
+
+				ext2_debug ("creating next block\n");
+
 				de = (struct ext2_dir_entry *) bh->b_data;
 				de->inode = 0;
 				de->rec_len = sb->s_blocksize;
 				dir->i_size = offset + sb->s_blocksize;
 				dir->i_dirt = 1;
+#if 0 /* XXX don't update any times until successful completion of syscall */
 				dir->i_ctime = CURRENT_TIME;
-			} else {
-#ifdef EXT2FS_DEBUG
-				printk ("ext2_add_entry: skipping to next block\n");
 #endif
+			} else {
+
+				ext2_debug ("skipping to next block\n");
+
 				de = (struct ext2_dir_entry *) bh->b_data;
 			}
 		}
@@ -234,14 +237,12 @@
 			brelse (bh);
 			return NULL;
 		}
-		if (de->inode) {
-			if (ext2_match (namelen, name, de)) {
+		if (de->inode != 0 && ext2_match (namelen, name, de)) {
 				*err = -EEXIST;
 				brelse (bh);
 				return NULL;
-			}
 		}
-		if ((!de->inode && de->rec_len >= rec_len) ||
+		if ((de->inode == 0 && de->rec_len >= rec_len) ||
 		    (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
 			offset += de->rec_len;
 			if (de->inode) {
@@ -254,8 +255,18 @@
 			}
 			de->inode = 0;
 			de->name_len = namelen;
-			for (i = 0; i < namelen ; i++)
-				de->name[i] = name [i];
+			memcpy (de->name, name, namelen);
+			/*
+			 * XXX shouldn't update any times until successful
+			 * completion of syscall, but too many callers depend
+			 * on this.
+			 *
+			 * XXX similarly, too many callers depend on
+			 * ext2_new_inode() setting the times, but error
+			 * recovery deletes the inode, so the worst that can
+			 * happen is that the times are slightly out of date
+			 * and/or different from the directory change time.
+			 */
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			dir->i_dirt = 1;
 			bh->b_dirt = 1;
@@ -275,7 +286,7 @@
  * previous entry
  */
 static int ext2_delete_entry (struct ext2_dir_entry * dir,
-			      struct buffer_head *bh)
+			      struct buffer_head * bh)
 {
 	struct ext2_dir_entry * de, * pde;
 	int i;
@@ -290,8 +301,8 @@
 		if (de == dir)  {
 			if (pde)
 				pde->rec_len += dir->rec_len;
-			else
-				dir->inode = 0;
+			/* XXX - must zero the inode number in every case !! */
+			dir->inode = 0;
 			return 0;
 		}
 		i += de->rec_len;
@@ -334,6 +345,10 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	brelse (bh);
 	iput (dir);
 	*result = inode;
@@ -381,7 +396,14 @@
 		init_fifo(inode);
 	if (S_ISBLK(mode) || S_ISCHR(mode))
 		inode->i_rdev = rdev;
+#if 0
+	/*
+	 * XXX we may as well use the times set by ext2_new_inode().  The
+	 * following usually does nothing, but sometimes it invalidates
+	 * inode->i_ctime.
+	 */
 	inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
 	inode->i_dirt = 1;
 	bh = ext2_add_entry (dir, name, len, &de, &err);
 	if (!bh) {
@@ -397,6 +419,10 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	brelse (bh);
 	iput (dir);
 	iput (inode);
@@ -429,7 +455,9 @@
 	}
 	inode->i_op = &ext2_dir_inode_operations;
 	inode->i_size = inode->i_sb->s_blocksize;
+#if 0 /* XXX as above */
 	inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
 	dir_block = ext2_bread (inode, 0, 1, &err);
 	if (!dir_block) {
 		iput (dir);
@@ -460,6 +488,7 @@
 	if (!bh) {
 		iput (dir);
 		inode->i_nlink = 0;
+		inode->i_dirt = 1;
 		iput (inode);
 		return err;
 	}
@@ -469,6 +498,10 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	dir->i_nlink++;
 	dir->i_dirt = 1;
 	iput (dir);
@@ -491,16 +524,16 @@
 	sb = inode->i_sb;
 	if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
 	    !(bh = ext2_bread (inode, 0, 0, &err))) {
-	    	printk ("warning - bad directory (dev %04x, dir %d)\n",
-			inode->i_dev, inode->i_ino);
+	    	ext2_warning (inode->i_sb, "empty_dir",
+			      "bad directory (dir %lu)", inode->i_ino);
 		return 1;
 	}
 	de = (struct ext2_dir_entry *) bh->b_data;
 	de1 = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
 	if (de->inode != inode->i_ino || !de1->inode || 
 	    strcmp (".", de->name) || strcmp ("..", de1->name)) {
-	    	printk ("warning - bad directory (dev %04x, dir %d)\n",
-			inode->i_dev, inode->i_ino);
+	    	ext2_warning (inode->i_sb, "empty_dir",
+			      "bad directory (dir %lu)", inode->i_ino);
 		return 1;
 	}
 	offset = de->rec_len + de1->rec_len;
@@ -571,30 +604,40 @@
 		retval = -ENOTEMPTY;
 		goto end_rmdir;
 	}
-	if (inode->i_count > 1 && inode->i_nlink <= 2) {
+	if (de->inode != inode->i_ino) {
+		retval = -ENOENT;
+		goto end_rmdir;
+	}
+	if (inode->i_count > 1) {
 		/* Are we deleting the last instance of a busy directory?
 		   Better clean up if so. */
 		/* Make directory empty (it will be truncated when finally
 		   dereferenced).  This also inhibits ext2_add_entry. */
 		inode->i_size = 0;
-#ifndef DONT_USE_DCACHE
-		ext2_dcache_remove(inode->i_dev, inode->i_ino, ".", 1);
-		ext2_dcache_remove(inode->i_dev, inode->i_ino, "..", 2);
-#endif
 	}
-	if (inode->i_nlink != 2)
-		printk ("empty  directory has nlink!=2 (%d)\n", inode->i_nlink);
-#ifndef DONT_USE_DCACHE
-	ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
-#endif
 	retval = ext2_delete_entry (de, bh);
 	if (retval)
 		goto end_rmdir;
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
+#ifndef DONT_USE_DCACHE
+	ext2_dcache_remove(inode->i_dev, inode->i_ino, ".", 1);
+	ext2_dcache_remove(inode->i_dev, inode->i_ino, "..", 2);
+#endif
+	if (inode->i_nlink != 2)
+		ext2_warning (inode->i_sb, "ext2_rmdir",
+			      "empty directory has nlink!=2 (%d)",
+			      inode->i_nlink);
+#ifndef DONT_USE_DCACHE
+	ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
+#endif
 	inode->i_nlink = 0;
 	inode->i_dirt = 1;
 	dir->i_nlink--;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 end_rmdir:
 	iput (dir);
@@ -635,22 +678,27 @@
 	    current->euid != dir->i_uid)
 		goto end_unlink;
 	if (!inode->i_nlink) {
-		printk ("Deleting nonexistent file (%04x:%d), %d\n",
-			inode->i_dev, inode->i_ino, inode->i_nlink);
+		ext2_warning (inode->i_sb, "ext2_unlink",
+			      "Deleting nonexistent file (%lu), %d",
+			      inode->i_ino, inode->i_nlink);
 		inode->i_nlink = 1;
 	}
-#ifndef DONT_USE_DCACHE
-	ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
-#endif
 	retval = ext2_delete_entry (de, bh);
 	if (retval)
 		goto end_unlink;
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
+#ifndef DONT_USE_DCACHE
+	ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
+#endif
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 	inode->i_nlink--;
 	inode->i_dirt = 1;
-	inode->i_ctime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime;
 	retval = 0;
 end_unlink:
 	brelse (bh);
@@ -680,9 +728,9 @@
 	     symname [l]; l++)
 		;
 	if (l >= EXT2_N_BLOCKS * sizeof (unsigned long)) {
-#ifdef EXT2FS_DEBUG
-		printk ("ext2_symlink: l=%d, normal symlink\n", l);
-#endif
+
+		ext2_debug ("l=%d, normal symlink\n", l);
+
 		name_block = ext2_bread (inode, 0, 1, &err);
 		if (!name_block) {
 			iput (dir);
@@ -694,9 +742,9 @@
 		link = name_block->b_data;
 	} else {
 		link = (char *) inode->u.ext2_i.i_data;
-#ifdef EXT2FS_DEBUG
-		printk ("ext2_symlink: l=%d, fast symlink\n", l);
-#endif
+
+		ext2_debug ("l=%d, fast symlink\n", l);
+
 	}
 	i = 0;
 	while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
@@ -731,6 +779,10 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	brelse (bh);
 	iput (dir);
 	iput (inode);
@@ -749,7 +801,7 @@
 		iput (dir);
 		return -EPERM;
 	}
-	if (oldinode->i_nlink > EXT2_LINK_MAX) {
+	if (oldinode->i_nlink >= EXT2_LINK_MAX) {
 		iput (oldinode);
 		iput (dir);
 		return -EMLINK;
@@ -773,6 +825,10 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
 	brelse (bh);
 	iput (dir);
 	oldinode->i_nlink++;
@@ -941,14 +997,27 @@
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	old_dir->i_dirt = 1;
 	old_bh->b_dirt = 1;
+	if (IS_SYNC(old_dir)) {
+		ll_rw_block (WRITE, 1, &old_bh);
+		wait_on_buffer (old_bh);
+	}
 	new_bh->b_dirt = 1;
+	if (IS_SYNC(new_dir)) {
+		ll_rw_block (WRITE, 1, &new_bh);
+		wait_on_buffer (new_bh);
+	}
 	if (dir_bh) {
 		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
 		dir_bh->b_dirt = 1;
 		old_dir->i_nlink--;
-		new_dir->i_nlink++;
 		old_dir->i_dirt = 1;
-		new_dir->i_dirt = 1;
+		if (new_inode) {
+			new_inode->i_nlink--;
+			new_inode->i_dirt = 1;
+		} else {
+			new_dir->i_nlink++;
+			new_dir->i_dirt = 1;
+		}
 	}
 	retval = 0;
 end_rename:
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
new file mode 100644
index 0000000..63bce7e
--- /dev/null
+++ b/fs/ext2/super.c
@@ -0,0 +1,535 @@
+/*
+ *  linux/fs/ext2/super.c
+ *
+ *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
+ *
+ *  from
+ *
+ *  linux/fs/minix/inode.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <stdarg.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+extern int vsprintf (char *, const char *, va_list);
+
+void ext2_error (struct super_block * sb, const char * function,
+		 const char * fmt, ...)
+{
+	char buf[1024];
+	va_list args;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+		sb->s_dirt = 1;
+	}
+	va_start (args, fmt);
+	vsprintf (buf, fmt, args);
+	va_end (args);
+	printk (
+#ifdef KERN_ERR
+		KERN_ERR
+#endif
+		"EXT2-fs error (device %d/%d): %s: %s\n",
+		MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf);
+}
+
+volatile void ext2_panic (struct super_block * sb, const char * function,
+			  const char * fmt, ...)
+{
+	char buf[1024];
+	va_list args;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+		sb->s_dirt = 1;
+	}
+	va_start (args, fmt);
+	vsprintf (buf, fmt, args);
+	va_end (args);
+	panic ("EXT2-fs panic (device %d/%d): %s: %s\n",
+	       MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf);
+}
+
+void ext2_warning (struct super_block * sb, const char * function,
+		   const char * fmt, ...)
+{
+	char buf[1024];
+	va_list args;
+
+	va_start (args, fmt);
+	vsprintf (buf, fmt, args);
+	va_end (args);
+	printk (
+#ifdef KERN_WARNING
+		KERN_WARNING
+#endif
+		"EXT2-fs warning (device %d/%d): %s: %s\n",
+		MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf);
+}
+
+void ext2_put_super (struct super_block * sb)
+{
+	int i;
+
+	lock_super (sb);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		sb->u.ext2_sb.s_es->s_state = sb->u.ext2_sb.s_mount_state;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	}
+#ifndef DONT_USE_DCACHE
+	ext2_dcache_invalidate (sb->s_dev);
+#endif
+	sb->s_dev = 0;
+	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+		if (sb->u.ext2_sb.s_group_desc[i])
+			brelse (sb->u.ext2_sb.s_group_desc[i]);
+	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+		if (sb->u.ext2_sb.s_inode_bitmap[i])
+			brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
+	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+		if (sb->u.ext2_sb.s_block_bitmap[i])
+			brelse (sb->u.ext2_sb.s_block_bitmap[i]);
+	brelse (sb->u.ext2_sb.s_sbh);
+	unlock_super (sb);
+	return;
+}
+
+static struct super_operations ext2_sops = { 
+	ext2_read_inode,
+	NULL,
+	ext2_write_inode,
+	ext2_put_inode,
+	ext2_put_super,
+	ext2_write_super,
+	ext2_statfs,
+	ext2_remount
+};
+
+#ifdef EXT2FS_PRE_02B_COMPAT
+
+static int convert_pre_02b_fs (struct super_block * sb,
+			       struct buffer_head * bh)
+{
+	struct ext2_super_block * es;
+	struct ext2_old_group_desc old_group_desc [BLOCK_SIZE / sizeof (struct ext2_old_group_desc)];
+	struct ext2_group_desc * gdp;
+	struct buffer_head * bh2;
+	int groups_count;
+	int i;
+
+	es = (struct ext2_super_block *) bh->b_data;
+	bh2 = bread (sb->s_dev, 2, BLOCK_SIZE);
+	if (!bh2) {
+		printk ("Cannot read descriptor blocks while converting !\n");
+		return 0;
+	}
+	memcpy (old_group_desc, bh2->b_data, BLOCK_SIZE);
+	groups_count = (sb->u.ext2_sb.s_blocks_count - 
+			sb->u.ext2_sb.s_first_data_block +
+			(EXT2_BLOCK_SIZE(sb) * 8) - 1) /
+				(EXT2_BLOCK_SIZE(sb) * 8);
+	memset (bh2->b_data, 0, BLOCK_SIZE);
+	gdp = (struct ext2_group_desc *) bh2->b_data;
+	for (i = 0; i < groups_count; i++) {
+		gdp[i].bg_block_bitmap = old_group_desc[i].bg_block_bitmap;
+		gdp[i].bg_inode_bitmap = old_group_desc[i].bg_inode_bitmap;
+		gdp[i].bg_inode_table = old_group_desc[i].bg_inode_table;
+		gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count;
+		gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count;
+	}
+	bh2->b_dirt = 1;
+	brelse (bh2);
+	es->s_magic = EXT2_SUPER_MAGIC;
+	bh->b_dirt = 1;
+	sb->s_magic = EXT2_SUPER_MAGIC;
+	return 1;
+}
+
+#endif
+
+/*
+ * This function has been shamelessly adapted from the msdos fs
+ */
+static int parse_options (char * options, unsigned long * sb_block,
+			  unsigned long * mount_options)
+{
+	char * this_char;
+	char * value;
+
+	if (!options)
+		return 1;
+	for (this_char = strtok (options, ",");
+	     this_char != NULL;
+	     this_char = strtok (NULL, ",")) {
+		if ((value = strchr (this_char, '=')) != NULL)
+			*value++ = 0;
+		if (!strcmp (this_char, "check"))
+			*mount_options |= EXT2_MOUNT_CHECK;
+		else if (!strcmp (this_char, "sb")) {
+			if (!value || !*value)
+				return 0;
+			*sb_block = simple_strtoul (value, &value, 0);
+			if (*value)
+				return 0;
+		}
+		else {
+			printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+struct super_block * ext2_read_super (struct super_block * s, void * data,
+				      int silent)
+{
+	struct buffer_head * bh;
+	struct ext2_super_block * es;
+	unsigned long sb_block = 1;
+	unsigned long logic_sb_block = 1;
+	int dev = s->s_dev;
+	int bh_count;
+	int i, j;
+#ifdef EXT2FS_PRE_02B_COMPAT
+	int fs_converted = 0;
+#endif
+
+	s->u.ext2_sb.s_mount_opt = 0;
+	if (!parse_options ((char *) data, &sb_block,
+	    &s->u.ext2_sb.s_mount_opt)) {
+		s->s_dev = 0;
+		return NULL;
+	}
+
+	lock_super (s);
+	set_blocksize (dev, BLOCK_SIZE);
+	if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
+		s->s_dev = 0;
+		unlock_super (s);
+		printk ("EXT2-fs: unable to read superblock\n");
+		return NULL;
+	}
+	es = (struct ext2_super_block *) bh->b_data;
+	/* Note: s_es must be initialized s_es as soon as possible because
+	   some ext2 macro-instructions depend on its value */
+	s->u.ext2_sb.s_es = es;
+	s->s_magic = es->s_magic;
+	if (s->s_magic != EXT2_SUPER_MAGIC
+#ifdef EXT2FS_PRE_02B_COMPAT
+	   && s->s_magic != EXT2_PRE_02B_MAGIC
+#endif
+	   ) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		if (!silent)
+			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+				dev);
+		return NULL;
+	}
+	s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
+	s->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(s);
+	if (s->s_blocksize != BLOCK_SIZE && 
+	    (s->s_blocksize == 1024 || s->s_blocksize == 2048 ||  
+	     s->s_blocksize == 4096)) {
+		unsigned long offset;
+
+		brelse (bh);
+		set_blocksize (dev, s->s_blocksize);
+		logic_sb_block = sb_block / s->s_blocksize;
+		offset = sb_block % s->s_blocksize;
+		bh = bread (dev, logic_sb_block, s->s_blocksize);
+		if(!bh)
+			return NULL;
+		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
+		s->u.ext2_sb.s_es = es;
+		if (es->s_magic != EXT2_SUPER_MAGIC) {
+			s->s_dev = 0;
+			unlock_super (s);
+			brelse (bh);
+			printk ("EXT2-fs: Magic mismatch, very weird !\n");
+			return NULL;
+		}
+	}
+	s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
+				   es->s_log_frag_size;
+	if (s->u.ext2_sb.s_frag_size)
+		s->u.ext2_sb.s_frags_per_block = s->s_blocksize /
+						   s->u.ext2_sb.s_frag_size;
+	else
+		s->s_magic = 0;
+	s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
+	s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
+	s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
+	s->u.ext2_sb.s_inodes_per_block = s->s_blocksize /
+					  sizeof (struct ext2_inode);
+	s->u.ext2_sb.s_desc_per_block = s->s_blocksize /
+					sizeof (struct ext2_group_desc);
+	s->u.ext2_sb.s_sbh = bh;
+	s->u.ext2_sb.s_es = es;
+	s->u.ext2_sb.s_mount_state = es->s_state;
+	s->u.ext2_sb.s_rename_lock = 0;
+	s->u.ext2_sb.s_rename_wait = NULL;
+#ifdef EXT2FS_PRE_02B_COMPAT
+	if (s->s_magic == EXT2_PRE_02B_MAGIC) {
+		if (es->s_blocks_count > 262144) {
+			 /* fs > 256 MB can't be converted */ 
+			s->s_dev = 0;
+			unlock_super (s);
+			brelse (bh);
+			printk ("EXT2-fs: trying to mount a pre-0.2b file"
+				"system which cannot be converted\n");
+			return NULL;
+		}
+		printk ("EXT2-fs: mounting a pre 0.2b file system, "
+			"will try to convert the structure\n");
+		if (!(s->s_flags & MS_RDONLY)) {
+			s->s_dev = 0;
+			unlock_super (s);
+			brelse (bh);
+			printk ("EXT2-fs: cannot convert a read-only fs\n");
+			return NULL;
+		}
+		if (!convert_pre_02b_fs (s, bh)) {
+			s->s_dev = 0;
+			unlock_super (s);
+			brelse (bh);
+			printk ("EXT2-fs: conversion failed !!!\n");
+			return NULL;
+		}
+		printk ("EXT2-fs: conversion succeeded !!!\n");
+		fs_converted = 1;
+	}
+#endif
+	if (s->s_magic != EXT2_SUPER_MAGIC) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		if (!silent)
+			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+				dev);
+		return NULL;
+	}
+	if (s->s_blocksize != bh->b_size) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		if (!silent)
+			printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
+				dev);
+		return NULL;
+	}
+
+	if (s->s_blocksize != s->u.ext2_sb.s_frag_size) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
+			s->u.ext2_sb.s_frag_size, s->s_blocksize);
+		return NULL;
+	}
+
+	s->u.ext2_sb.s_groups_count = (es->s_blocks_count -
+				       es->s_first_data_block +
+				       EXT2_BLOCKS_PER_GROUP(s) - 1) /
+				       EXT2_BLOCKS_PER_GROUP(s);
+	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+		s->u.ext2_sb.s_group_desc[i] = NULL;
+	bh_count = (s->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(s) - 1) /
+		   EXT2_DESC_PER_BLOCK(s);
+	if (bh_count > EXT2_MAX_GROUP_DESC) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		printk ("EXT2-fs: file system is too big\n");
+		return NULL;
+	}
+	for (i = 0; i < bh_count; i++) {
+		s->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
+						      s->s_blocksize);
+		if (!s->u.ext2_sb.s_group_desc[i]) {
+			s->s_dev = 0;
+			unlock_super (s);
+			for (j = 0; j < i; j++)
+				brelse (s->u.ext2_sb.s_group_desc[i]);
+			brelse (bh);
+			printk ("EXT2-fs: unable to read group descriptors\n");
+			return NULL;
+		}
+	}
+	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
+		s->u.ext2_sb.s_inode_bitmap_number[i] = 0;
+		s->u.ext2_sb.s_inode_bitmap[i] = NULL;
+		s->u.ext2_sb.s_block_bitmap_number[i] = 0;
+		s->u.ext2_sb.s_block_bitmap[i] = NULL;
+	}
+	s->u.ext2_sb.s_loaded_inode_bitmaps = 0;
+	s->u.ext2_sb.s_loaded_block_bitmaps = 0;
+	unlock_super (s);
+	/* set up enough so that it can read an inode */
+	s->s_dev = dev;
+	s->s_op = &ext2_sops;
+	if (!(s->s_mounted = iget (s, EXT2_ROOT_INO))) {
+		s->s_dev = 0;
+		for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+			if (s->u.ext2_sb.s_group_desc[i])
+				brelse (s->u.ext2_sb.s_group_desc[i]);
+		brelse (bh);
+		printk ("EXT2-fs: get root inode failed\n");
+		return NULL;
+	}
+	if (!(s->s_flags & MS_RDONLY)) {
+		es->s_state &= ~EXT2_VALID_FS;
+		if (!es->s_max_mnt_count)
+			es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
+		es->s_mnt_count++;
+		es->s_mtime = CURRENT_TIME;
+		bh->b_dirt = 1;
+		s->s_dirt = 1;
+	}
+#ifdef EXT2FS_PRE_02B_COMPAT
+	if (fs_converted) {
+		for (i = 0; i < bh_count; i++)
+			s->u.ext2_sb.s_group_desc[i]->b_dirt = 1;
+		s->s_dirt = 1;
+	}
+#endif
+	if (!(s->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
+		printk ("EXT2-fs warning: mounting unchecked file system, "
+			"running e2fsck is recommended\n");
+ 	else if (s->u.ext2_sb.s_mount_state & EXT2_ERROR_FS)
+		printk ("EXT2-fs warning: mounting file system with errors, "
+			"running e2fsck is recommended\n");
+	else if (es->s_mnt_count >= es->s_max_mnt_count)
+		printk ("EXT2-fs warning: maximal mount count reached, "
+                        "running e2fsck is recommended\n");
+	if (s->u.ext2_sb.s_mount_opt & EXT2_MOUNT_CHECK) {
+		printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, bpg=%lu, ipg=%lu]\n",
+			EXT2FS_VERSION, EXT2FS_DATE, s->s_blocksize,
+			s->u.ext2_sb.s_frag_size, s->u.ext2_sb.s_groups_count,
+			EXT2_BLOCKS_PER_GROUP(s), EXT2_INODES_PER_GROUP(s));
+		ext2_check_blocks_bitmap (s);
+		ext2_check_inodes_bitmap (s);
+	}
+	return s;
+}
+
+static void ext2_commit_super (struct super_block * sb,
+			       struct ext2_super_block * es)
+{
+	es->s_wtime = CURRENT_TIME;
+	sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	sb->s_dirt = 0;
+}
+
+/*
+ * In the second extended file system, it is not necessary to
+ * write the super block since we use a mapping of the
+ * disk super block in a buffer.
+ *
+ * However, this function is still used to set the fs valid
+ * flags to 0.  We need to set this flag to 0 since the fs
+ * may have been checked while mounted and e2fsck may have
+ * set s_state to EXT2_VALID_FS after some corrections.
+ */
+
+void ext2_write_super (struct super_block * sb)
+{
+	struct ext2_super_block * es;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		es = sb->u.ext2_sb.s_es;
+
+		ext2_debug ("setting valid to 0\n");
+
+		if (es->s_state & EXT2_VALID_FS) {
+			es->s_state &= ~EXT2_VALID_FS;
+			es->s_mtime = CURRENT_TIME;
+		}
+		ext2_commit_super (sb, es);
+	}
+	sb->s_dirt = 0;
+}
+
+int ext2_remount (struct super_block * sb, int * flags)
+{
+	struct ext2_super_block * es;
+
+	es = sb->u.ext2_sb.s_es;
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+	if (*flags & MS_RDONLY) {
+		if (es->s_state & EXT2_VALID_FS ||
+		    !(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
+			return 0;
+		/* OK, we are remounting a valid rw partition rdonly, so set
+		   the rdonly flag and then mark the partition as valid
+		   again. */
+		es->s_state = sb->u.ext2_sb.s_mount_state;
+		es->s_mtime = CURRENT_TIME;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+		sb->s_dirt = 1;
+		ext2_commit_super (sb, es);
+	}
+	else {
+		/* Mounting a RDONLY partition read-write, so reread and
+		   store the current valid flag.  (It may have been changed 
+		   by e2fsck since we originally mounted the partition.)  */
+		sb->u.ext2_sb.s_mount_state = es->s_state;
+		es->s_state &= ~EXT2_VALID_FS;
+		if (!es->s_max_mnt_count)
+			es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
+		es->s_mnt_count++;
+		es->s_mtime = CURRENT_TIME;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+		sb->s_dirt = 1;
+		if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
+			printk ("EXT2-fs warning: remounting unchecked fs, "
+				"running e2fsck is recommended\n");
+		else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
+			printk ("EXT2-fs warning: remounting fs with errors, "
+				"running e2fsck is recommended\n");
+		else if (es->s_mnt_count >= es->s_max_mnt_count)
+			printk ("EXT2-fs warning: maximal mount count reached, "
+                        	"running e2fsck is recommended\n");
+	}
+	return 0;
+}
+
+void ext2_statfs (struct super_block * sb, struct statfs * buf)
+{
+	long tmp;
+
+	put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type);
+	put_fs_long (sb->s_blocksize, &buf->f_bsize);
+	put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count, &buf->f_blocks);
+	tmp = ext2_count_free_blocks (sb);
+	put_fs_long (tmp, &buf->f_bfree);
+	if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count)
+		put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count,
+			     &buf->f_bavail);
+	else
+		put_fs_long (0, &buf->f_bavail);
+	put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files);
+	put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree);
+	put_fs_long (EXT2_NAME_LEN, &buf->f_namelen);
+	/* Don't know what value to put in buf->f_fsid */
+}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index e802842..d6cff0a 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -15,9 +15,9 @@
 #include <asm/segment.h>
 
 #include <linux/errno.h>
-#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
+#include <linux/sched.h>
 #include <linux/stat.h>
 
 static int ext2_readlink (struct inode *, char *, int);
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index 92fc244..9b67d35 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -10,12 +10,21 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
-#include <linux/sched.h>
-#include <linux/ext2_fs.h>
-#include <linux/tty.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
 #include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#define clear_block(addr,size,value) \
+	__asm__("cld\n\t" \
+		"rep\n\t" \
+		"stosl" \
+		: \
+		:"a" (value), "c" (size / 4), "D" ((long) (addr)) \
+		:"cx", "di")
 
 /*
  * Truncate has the most races in the whole filesystem: coding it is
@@ -47,7 +56,12 @@
 		tmp = *p;
 		if (!tmp)
 			continue;
-		bh = get_hash_table (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
+			bh = getblk (inode->i_dev, tmp,
+				     inode->i_sb->s_blocksize);
+		else
+			bh = get_hash_table (inode->i_dev, tmp,
+					     inode->i_sb->s_blocksize);
 		if (i < direct_block) {
 			brelse (bh);
 			goto repeat;
@@ -60,6 +74,11 @@
 		*p = 0;
 		inode->i_blocks -= blocks;
 		inode->i_dirt = 1;
+		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
+			clear_block (bh->b_data, inode->i_sb->s_blocksize,
+				     CURRENT_TIME);
+			bh->b_dirt = 1;
+		}
 		brelse (bh);
 		ext2_free_block (inode->i_sb, tmp);
 	}
@@ -100,8 +119,12 @@
 		tmp = *ind;
 		if (!tmp)
 			continue;
-		bh = get_hash_table (inode->i_dev, tmp,
+		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
+			bh = getblk (inode->i_dev, tmp,
 				     inode->i_sb->s_blocksize);
+		else
+			bh = get_hash_table (inode->i_dev, tmp,
+					     inode->i_sb->s_blocksize);
 		if (i < indirect_block) {
 			brelse (bh);
 			goto repeat;
@@ -113,6 +136,11 @@
 		}
 		*ind = 0;
 		ind_bh->b_dirt = 1;
+		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
+			clear_block (bh->b_data, inode->i_sb->s_blocksize,
+				     CURRENT_TIME);
+			bh->b_dirt = 1;
+		}
 		brelse (bh);
 		ext2_free_block (inode->i_sb, tmp);
 		inode->i_blocks -= blocks;
@@ -132,10 +160,14 @@
 			inode->i_dirt = 1;
 			ext2_free_block (inode->i_sb, tmp);
 		}
+	if (IS_SYNC(inode) && ind_bh->b_dirt) {
+		ll_rw_block (WRITE, 1, &ind_bh);
+		wait_on_buffer (ind_bh);
+	}
 	brelse (ind_bh);
 	return retry;
 }
-		
+
 static int trunc_dindirect (struct inode * inode, int offset,
 			    unsigned long * p)
 {
@@ -188,6 +220,10 @@
 			inode->i_dirt = 1;
 			ext2_free_block (inode->i_sb, tmp);
 		}
+	if (IS_SYNC(inode) && dind_bh->b_dirt) {
+		ll_rw_block (WRITE, 1, &dind_bh);
+		wait_on_buffer (dind_bh);
+	}
 	brelse (dind_bh);
 	return retry;
 }
@@ -225,8 +261,6 @@
 			goto repeat;
 		tind = i + (unsigned long *) tind_bh->b_data;
 		retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
-/*			addr_per_block + addr_per_block * addr_per_block +
-			(i * (addr_per_block * addr_per_block)), tind); */
 			addr_per_block + (i + 1) * addr_per_block * addr_per_block,
 			tind);
 		tind_bh->b_dirt = 1;
@@ -245,6 +279,10 @@
 			inode->i_dirt = 1;
 			ext2_free_block (inode->i_sb, tmp);
 		}
+	if (IS_SYNC(inode) && tind_bh->b_dirt) {
+		ll_rw_block (WRITE, 1, &tind_bh);
+		wait_on_buffer (tind_bh);
+	}
 	brelse (tind_bh);
 	return retry;
 }
@@ -266,6 +304,8 @@
 		retry |= trunc_tindirect (inode);
 		if (!retry)
 			break;
+		if (IS_SYNC(inode) && inode->i_dirt)
+			ext2_sync_inode (inode);
 		current->counter = 0;
 		schedule ();
 	}
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 7c9f71c..671d76e 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -32,6 +32,9 @@
 #ifdef CONFIG_ISO9660_FS
 #include <linux/iso_fs.h>
 #endif
+#ifdef CONFIG_HPFS_FS
+#include <linux/hpfs_fs.h>
+#endif
 
 struct file_system_type file_systems[] = {
 #ifdef CONFIG_MINIX_FS
@@ -58,5 +61,8 @@
 #ifdef CONFIG_ISO9660_FS
 	{isofs_read_super,	"iso9660",	1},
 #endif
+#ifdef CONFIG_HPFS_FS
+	{hpfs_read_super,	"hpfs",		1},
+#endif
 	{NULL,			NULL,		0}
 };
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile
new file mode 100644
index 0000000..94ab74d
--- /dev/null
+++ b/fs/hpfs/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the linux HPFS 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...
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+.s.o:
+	$(AS) -o $*.o $<
+
+OBJS=	hpfs_fs.o
+
+hpfs.o: $(OBJS)
+	ln -f hpfs_fs.o hpfs.o
+
+dep:
+	$(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h
new file mode 100644
index 0000000..b01e8d7
--- /dev/null
+++ b/fs/hpfs/hpfs.h
@@ -0,0 +1,494 @@
+/* The paper
+
+     Duncan, Roy
+     Design goals and implementation of the new High Performance File System
+     Microsoft Systems Journal  Sept 1989  v4 n5 p1(13)
+
+   describes what HPFS looked like when it was new, and it is the source
+   of most of the information given here.  The rest is conjecture.
+
+   For definitive information on the Duncan paper, see it, not this file.
+   For definitive information on HPFS, ask somebody else -- this is guesswork.
+   There are certain to be many mistakes. */
+
+/* Notation */
+
+typedef unsigned secno;			/* sector number, partition relative */
+
+typedef secno dnode_secno;		/* sector number of a dnode */
+typedef secno fnode_secno;		/* sector number of an fnode */
+typedef secno anode_secno;		/* sector number of an anode */
+
+/* sector 0 */
+
+/* The boot block is very like a FAT boot block, except that the
+   29h signature byte is 28h instead, and the ID string is "HPFS". */
+
+struct hpfs_boot_block
+{
+  unsigned char jmp[3];
+  unsigned char oem_id[8];
+  unsigned char bytes_per_sector[2];	/* 512 */
+  unsigned char sectors_per_cluster;
+  unsigned char n_reserved_sectors[2];
+  unsigned char n_fats;
+  unsigned char n_rootdir_entries[2];
+  unsigned char n_sectors_s[2];
+  unsigned char media_byte;
+  unsigned short sectors_per_fat;
+  unsigned short sectors_per_track;
+  unsigned short heads_per_cyl;
+  unsigned int n_hidden_sectors;
+  unsigned int n_sectors_l;		/* size of partition */
+  unsigned char drive_number;
+  unsigned char mbz;
+  unsigned char sig_28h;		/* 28h */
+  unsigned char vol_serno[4];
+  unsigned char vol_label[11];
+  unsigned char sig_hpfs[8];		/* "HPFS    " */
+  unsigned char pad[448];
+  unsigned short magic;			/* aa55 */
+};
+
+
+/* sector 16 */
+
+/* The super block has the pointer to the root directory. */
+
+#define SB_MAGIC 0xf995e849
+
+struct hpfs_super_block
+{
+  unsigned magic;			/* f995 e849 */
+  unsigned magic1;			/* fa53 e9c5, more magic? */
+  unsigned huh202;			/* ?? 202 = N. of B. in 1.00390625 S.*/
+  fnode_secno root;			/* fnode of root directory */
+  secno n_sectors;			/* size of filesystem */
+  unsigned n_badblocks;			/* number of bad blocks */
+  secno bitmaps;			/* pointers to free space bit maps */
+  unsigned zero1;			/* 0 */
+  secno badblocks;			/* bad block list */
+  unsigned zero3;			/* 0 */
+  time_t last_chkdsk;			/* date last checked, 0 if never */
+  unsigned zero4;			/* 0 */
+  secno n_dir_band;			/* number of sectors in dir band */
+  secno dir_band_start;			/* first sector in dir band */
+  secno dir_band_end;			/* last sector in dir band */
+  secno dir_band_bitmap;		/* free space map, 1 dnode per bit */
+  unsigned zero5[8];			/* 0 */
+  secno scratch_dnodes;			/* ?? 8 preallocated sectors near dir
+					   band, 4-aligned. */
+  unsigned zero6[103];			/* 0 */
+};
+
+
+/* sector 17 */
+
+/* The spare block has pointers to spare sectors.  */
+
+#define SP_MAGIC 0xf9911849
+
+struct hpfs_spare_block
+{
+  unsigned magic;			/* f991 1849 */
+  unsigned magic1;			/* fa52 29c5, more magic? */
+  unsigned dirty;			/* 0 clean, 1 "improperly stopped" */
+
+  secno hotfix_map;			/* info about remapped bad sectors */
+  unsigned n_spares_used;		/* number of hotfixes */
+  unsigned n_spares;			/* number of spares in hotfix map */
+  unsigned n_dnode_spares_free;		/* spare dnodes unused */
+  unsigned n_dnode_spares;		/* length of spare_dnodes[] list,
+					   follows in this block*/
+  secno code_page_dir;			/* code page directory block */
+  unsigned n_code_pages;		/* number of code pages */
+  unsigned large_numbers[2];		/* ?? */
+  unsigned zero1[15];
+  dnode_secno spare_dnodes[20];		/* emergency free dnode list */
+  unsigned zero2[81];			/* room for more? */
+};
+
+/* The bad block list is 4 sectors long.  The first word must be zero,
+   the remaining words give n_badblocks bad block numbers.
+   I bet you can see it coming... */
+
+#define BAD_MAGIC 0
+       
+/* The hotfix map is 4 sectors long.  It looks like
+
+       secno from[n_spares];
+       secno to[n_spares];
+
+   The to[] list is initalized to point to n_spares preallocated empty
+   sectors.  The from[] list contains the sector numbers of bad blocks
+   which have been remapped to corresponding sectors in the to[] list.
+   n_spares_used gives the length of the from[] list. */
+
+
+/* Sectors 18 and 19 are preallocated and unused.
+   Maybe they're spares for 16 and 17, but simple substitution fails. */
+
+
+/* The code page info pointed to by the spare block consists of an index
+   block and blocks containing character maps.  The following is pretty
+   sketchy, but Linux is Latin-1 so it doesn't matter. */
+
+/* block pointed to by spareblock->code_page_dir */
+
+#define CP_DIR_MAGIC 0x494521f7
+
+struct code_page_directory
+{
+  unsigned magic;			/* 4945 21f7 */
+  unsigned n_code_pages;		/* number of pointers following */
+  unsigned zero1[2];
+  struct {
+    unsigned short ix;			/* index */
+    unsigned short code_page_number;	/* code page number */
+    unsigned bounds;			/* matches corresponding word
+					   in data block */
+    secno code_page_data;		/* sector number of a code_page_data
+					   containing c.p. array */
+    unsigned index;			/* index in c.p. array in that sector*/
+  } array[31];				/* unknown length */
+};
+
+/* blocks pointed to by code_page_directory */
+
+#define CP_DATA_MAGIC 0x894521f7
+
+struct code_page_data
+{
+  unsigned magic;			/* 8945 21f7 */
+  unsigned n_used;			/* # elements used in c_p_data[] */
+  unsigned bounds[3];			/* looks a bit like
+					     (beg1,end1), (beg2,end2)
+					   one byte each */
+  unsigned short offs[3];		/* offsets from start of sector
+					   to start of c_p_data[ix] */
+  struct {
+    unsigned short ix;			/* index */
+    unsigned short code_page_number;	/* code page number */
+    unsigned short zero1;
+    unsigned char map[128];		/* map for chars 80..ff */
+    unsigned short zero2;
+  } code_page[3];
+  unsigned char incognita[78];
+};
+
+
+/* Free space bitmaps are 4 sectors long, which is 16384 bits.
+   16384 sectors is 8 meg, and each 8 meg band has a 4-sector bitmap.
+   Bit order in the maps is little-endian.  0 means taken, 1 means free.
+
+   Bit map sectors are marked allocated in the bit maps, and so are sectors 
+   off the end of the partition.
+
+   Band 0 is sectors 0-3fff, its map is in sectors 18-1b.
+   Band 1 is 4000-7fff, its map is in 7ffc-7fff.
+   Band 2 is 8000-ffff, its map is in 8000-8003.
+   The remaining bands have maps in their first (even) or last (odd) 4 sectors
+     -- if the last, partial, band is odd its map is in its last 4 sectors.
+
+   The bitmap locations are given in a table pointed to by the super block.
+   No doubt they aren't constrained to be at 18, 7ffc, 8000, ...; that is
+   just where they usually are.
+
+   The "directory band" is a bunch of sectors preallocated for dnodes.
+   It has a 4-sector free space bitmap of its own.  Each bit in the map
+   corresponds to one 4-sector dnode, bit 0 of the map corresponding to
+   the first 4 sectors of the directory band.  The entire band is marked
+   allocated in the main bitmap.   The super block gives the locations
+   of the directory band and its bitmap.  ("band" doesn't mean it is
+   8 meg long; it isn't.)  */
+
+
+/* dnode: directory.  4 sectors long */
+
+/* A directory is a tree of dnodes.  The fnode for a directory
+   contains one pointer, to the root dnode of the tree.  The fnode
+   never moves, the dnodes do the B-tree thing, splitting and merging
+   as files are added and removed.  */
+
+#define DNODE_MAGIC   0x77e40aae
+
+struct dnode {
+  unsigned magic;			/* 77e4 0aae */
+  unsigned first_free;			/* offset from start of dnode to
+					   first free dir entry */
+  unsigned increment_me;		/* some kind of activity counter?
+					   Neither HPFS.IFS nor CHKDSK cares
+					   if you change this word */
+  secno up;				/* (root dnode) directory's fnode
+					   (nonroot) parent dnode */
+  dnode_secno self;			/* pointer to this dnode */
+  unsigned char dirent[2028];		/* one or more dirents */
+};
+
+struct hpfs_dirent {
+  unsigned short length;		/* offset to next dirent */
+  unsigned first: 1;			/* set on phony ^A^A (".") entry */
+  unsigned flag1: 1;
+  unsigned down: 1;			/* down pointer present (after name) */
+  unsigned last: 1;			/* set on phony \377 entry */
+  unsigned flag4: 1;
+  unsigned flag5: 1;
+  unsigned flag6: 1;
+  unsigned has_needea: 1;		/* ?? some EA has NEEDEA set
+					   I have no idea why this is
+					   interesting in a dir entry */
+  unsigned read_only: 1;		/* dos attrib */
+  unsigned hidden: 1;			/* dos attrib */
+  unsigned system: 1;			/* dos attrib */
+  unsigned flag11: 1;			/* would be volume label dos attrib */
+  unsigned directory: 1;		/* dos attrib */
+  unsigned archive: 1;			/* dos attrib */
+  unsigned not_8x3: 1;			/* name is not 8.3 */
+  unsigned flag15: 1;
+  fnode_secno fnode;			/* fnode giving allocation info */
+  time_t write_date;			/* mtime */
+  unsigned file_size;			/* file length, bytes */
+  time_t read_date;			/* atime */
+  time_t creation_date;			/* ctime */
+  unsigned ea_size;			/* total EA length, bytes */
+  unsigned char zero1;
+  unsigned char locality;		/* 0=unk 1=seq 2=random 3=both */
+  unsigned char namelen, name[1];	/* file name */
+  /* dnode_secno down;	  btree down pointer, if present,
+     			  follows name on next word boundary, or maybe it's
+			  precedes next dirent, which is on a word boundary. */
+};
+
+/* The b-tree down pointer from a dir entry */
+
+static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+{
+  return *(dnode_secno *) ((void *) de + de->length - 4);
+}
+
+/* The first dir entry in a dnode */
+
+static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+{
+  return (void *) dnode->dirent;
+}
+
+/* The end+1 of the dir entries */
+
+static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+{
+  return (void *) dnode + dnode->first_free;
+}
+
+/* The dir entry after dir entry de */
+
+static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+{
+  return (void *) de + de->length;
+}
+
+
+/* B+ tree: allocation info in fnodes and anodes */
+
+/* dnodes point to fnodes which are responsible for listing the sectors
+   assigned to the file.  This is done with trees of (length,address)
+   pairs.  (Actually triples, of (length, file-address, disk-address)
+   which can represent holes.  Find out if HPFS does that.)
+   At any rate, fnodes contain a small tree; if subtrees are needed
+   they occupy essentially a full block in anodes.  A leaf-level tree node
+   has 3-word entries giving sector runs, a non-leaf node has 2-word
+   entries giving subtree pointers.  A flag in the header says which. */
+
+struct bplus_leaf_node
+{
+  unsigned file_secno;			/* first file sector in extent */
+  unsigned length;			/* length, sectors */
+  secno disk_secno;			/* first corresponding disk sector */
+};
+
+struct bplus_internal_node
+{
+  unsigned file_secno;			/* subtree maps sectors < this  */
+  anode_secno down;			/* pointer to subtree */
+};
+
+struct bplus_header
+{
+  unsigned flag0: 1;
+  unsigned flag1: 1;
+  unsigned flag2: 1;
+  unsigned flag3: 1;
+  unsigned flag4: 1;
+  unsigned fnode_parent: 1;		/* ? we're pointed to by an fnode,
+					   the data btree or some ea or the
+					   main ea bootage pointer ea_secno */
+					/* also can get set in fnodes, which
+					   may be a chkdsk glitch or may mean
+					   this bit is irrelevant in fnodes,
+					   or this interpretation is all wet */
+  unsigned flag6: 1;
+  unsigned internal: 1;			/* 1 -> (internal) tree of anodes
+					   0 -> (leaf) list of extents */
+  unsigned char fill[3];
+  unsigned char n_free_nodes;		/* free nodes in following array */
+  unsigned char n_used_nodes;		/* used nodes in following array */
+  unsigned short first_free;		/* offset from start of header to
+					   first free node in array */
+  union {
+    struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
+					       subtree pointers */
+    struct bplus_leaf_node external[0];	    /* (external) 3-word entries giving
+					       sector runs */
+  } u;
+};
+
+/* fnode: root of allocation b+ tree, and EA's */
+
+/* Every file and every directory has one fnode, pointed to by the directory
+   entry and pointing to the file's sectors or directory's root dnode.  EA's
+   are also stored here, and there are said to be ACL's somewhere here too. */
+
+#define FNODE_MAGIC 0xf7e40aae
+
+struct fnode
+{
+  unsigned magic;			/* f7e4 0aae */
+  unsigned zero1[2];
+  unsigned char len, name[15];		/* true length, truncated name */
+  fnode_secno up;			/* pointer to file's directory fnode */
+  unsigned zero2[3];
+  unsigned ea_size_l;			/* length of disk-resident ea's */
+  secno ea_secno;			/* first sector of disk-resident ea's*/
+  unsigned short ea_size_s;		/* length of fnode-resident ea's */
+
+  unsigned flag0: 1;
+  unsigned ea_anode: 1;			/* 1 -> ea_secno is an anode */
+  unsigned flag2: 1;
+  unsigned flag3: 1;
+  unsigned flag4: 1;
+  unsigned flag5: 1;
+  unsigned flag6: 1;
+  unsigned flag7: 1;
+  unsigned dirflag: 1;			/* 1 -> directory.  first & only extent
+					   points to dnode. */
+  unsigned flag9: 1;
+  unsigned flag10: 1;
+  unsigned flag11: 1;
+  unsigned flag12: 1;
+  unsigned flag13: 1;
+  unsigned flag14: 1;
+  unsigned flag15: 1;
+
+  struct bplus_header btree;		/* b+ tree, 8 extents or 12 subtrees */
+  union {
+    struct bplus_leaf_node external[8];
+    struct bplus_internal_node internal[12];
+  } u;
+
+  unsigned file_size;			/* file length, bytes */
+  unsigned n_needea;			/* number of EA's with NEEDEA set */
+  unsigned zero4[4];
+  unsigned ea_offs;			/* offset from start of fnode
+					   to first fnode-resident ea */
+  unsigned zero5[2];
+  unsigned char ea[316];		/* zero or more EA's, packed together
+					   with no alignment padding.
+					   (Do not use this name, get here
+					   via fnode + ea_offs. I think.) */
+};
+
+
+/* anode: 99.44% pure allocation tree */
+
+#define ANODE_MAGIC 0x37e40aae
+
+struct anode
+{
+  unsigned magic;			/* 37e4 0aae */
+  anode_secno self;			/* pointer to this anode */
+  secno up;				/* parent anode or fnode */
+
+  struct bplus_header btree;		/* b+tree, 40 extents or 60 subtrees */
+  union {
+    struct bplus_leaf_node external[40];
+    struct bplus_internal_node internal[60];
+  } u;
+
+  unsigned fill[3];			/* unused */
+};
+
+
+/* extended attributes.
+
+   A file's EA info is stored as a list of (name,value) pairs.  It is
+   usually in the fnode, but (if it's large) it is moved to a single
+   sector run outside the fnode, or to multiple runs with an anode tree
+   that points to them.
+
+   The value of a single EA is stored along with the name, or (if large)
+   it is moved to a single sector run, or multiple runs pointed to by an
+   anode tree, pointed to by the value field of the (name,value) pair.
+
+   Flags in the EA tell whether the value is immediate, in a single sector
+   run, or in multiple runs.  Flags in the fnode tell whether the EA list
+   is immediate, in a single run, or in multiple runs. */
+
+struct extended_attribute
+{
+  unsigned indirect: 1;			/* 1 -> value gives sector number
+					   where real value starts */
+  unsigned anode: 1;			/* 1 -> sector is an anode
+					   that points to fragmented value */
+  unsigned flag2: 1;
+  unsigned flag3: 1;
+  unsigned flag4: 1;
+  unsigned flag5: 1;
+  unsigned flag6: 1;
+  unsigned needea: 1;			/* required ea */
+  unsigned char namelen;		/* length of name, bytes */
+  unsigned short valuelen;		/* length of value, bytes */
+  /*
+    unsigned char name[namelen];	ascii attrib name
+    unsigned char nul;			terminating '\0', not counted
+    unsigned char value[valuelen];	value, arbitrary
+      if this.indirect, valuelen is 8 and the value is
+        unsigned length;		real length of value, bytes
+        secno secno;			sector address where it starts
+      if this.anode, the above sector number is the root of an anode tree
+        which points to the value.
+  */
+};
+
+static inline unsigned char *ea_name (struct extended_attribute *ea)
+{
+  return (void *) ea + sizeof *ea;
+}
+
+static inline unsigned char *ea_value (struct extended_attribute *ea)
+{
+  return (void *) ea + sizeof *ea + ea->namelen + 1;
+}
+
+static inline struct extended_attribute *
+    ea_next_ea (struct extended_attribute *ea)
+{
+  return (void *) ea + sizeof *ea + ea->namelen + 1 + ea->valuelen;
+}
+
+static inline unsigned ea_indirect_length (struct extended_attribute *ea)
+{
+  unsigned *v = (void *) ea_value (ea);
+  return v[0];
+}
+
+static inline secno ea_indirect_secno (struct extended_attribute *ea)
+{
+  unsigned *v = (void *) ea_value (ea);
+  return v[1];
+}
+
+/*
+   Local Variables:
+   comment-column: 40
+   End:
+*/
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
new file mode 100644
index 0000000..0bd09f2
--- /dev/null
+++ b/fs/hpfs/hpfs_fs.c
@@ -0,0 +1,1724 @@
+/*
+ *  linux/fs/hpfs/hpfs_fs.c
+ *  read-only HPFS
+ *  version 1.0
+ *
+ *  Chris Smith 1993
+ *
+ *  Sources & references:
+ *   Duncan, _Design ... of HPFS_, MSSJ 4(5)   (C) 1989 Microsoft Corp
+ *   linux/fs/minix  Copyright (C) 1991, 1992, 1993  Linus Torvalds
+ *   linux/fs/msdos  Written 1992, 1993 by Werner Almesberger
+ *   linux/fs/isofs  Copyright (C) 1991  Eric Youngdale
+ */
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+
+#include "hpfs.h"
+
+/* 
+ * HPFS is a mixture of 512-byte blocks and 2048-byte blocks.  The 2k blocks
+ * are used for directories and bitmaps.  For bmap to work, we must run the
+ * file system with 512-byte blocks.  The 2k blocks are assembled in buffers
+ * obtained from kmalloc.
+ *
+ * For a file's i-number we use the sector number of its fnode, coded.
+ * (Directory ino's are even, file ino's are odd, and ino >> 1 is the
+ * sector address of the fnode.  This is a hack to allow lookup() to
+ * tell read_inode() whether it is necessary to read the fnode.)
+ *
+ * The map_xxx routines all read something into a buffer and return a
+ * pointer somewhere in the buffer.  The caller must do the brelse.
+ * The other routines are balanced.
+ *
+ * For details on the data structures see hpfs.h and the Duncan paper.
+ *
+ * Overview
+ *
+ * [ The names of these data structures, except fnode, are not Microsoft's
+ * or IBM's.  I don't know what names they use.  The semantics described
+ * here are those of this implementation, and any coincidence between it
+ * and real HPFS is to be hoped for but not guaranteed by me, and
+ * certainly not guaranteed by MS or IBM.  Who know nothing about this. ]
+ *
+ * [ Also, the following will make little sense if you haven't read the
+ * Duncan paper, which is excellent. ]
+ *
+ * HPFS is a tree.  There are 3 kinds of nodes.  A directory is a tree
+ * of dnodes, and a file's allocation info is a tree of sector runs
+ * stored in fnodes and anodes.
+ *
+ * The top pointer is in the super block, it points to the fnode of the
+ * root directory.
+ *
+ * The root directory -- all directories -- gives file names, dates &c,
+ * and fnode addresses.  If the directory fits in one dnode, that's it,
+ * otherwise the top dnode points to other dnodes, forming a tree.  A
+ * dnode tree (one directory) might look like
+ *
+ *     ((a b c) d (e f g) h (i j) k l (m n o p))
+ *
+ * The subtrees appear between the files.  Each dir entry contains, along
+ * with the name and fnode, a dnode pointer to the subtree that precedes it
+ * (if there is one; a flag tells that).  The first entry in every directory
+ * is ^A^A, the "." entry for the directory itself.  The last entry in every
+ * dnode is \377, a fake entry whose only valid fields are the bit marking
+ * it last and the down pointer to the subtree preceding it, if any.
+ *
+ * The "value" field of directory entries is an fnode address.  The fnode
+ * tells where the sectors of the file are.  The fnode for a subdirectory
+ * contains one pointer, to the root dnode of the subdirectory.  The fnode
+ * for a data file contains, in effect, a tiny anode.  (Most of the space
+ * in fnodes is for extended attributes.)
+ *
+ * anodes and the anode part of fnodes are trees of extents.  An extent
+ * is a (length, disk address) pair, labeled with the file address being
+ * mapped.  E.g.,
+ *
+ *     (0: 3@1000  3: 1@2000  4: 2@10)
+ *
+ * means the file:disk sector map (0:1000 1:1001 2:1002 3:2000 4:10 5:11).
+ *
+ * There is space for 8 file:len@disk triples in an fnode, or for 40 in an
+ * anode.  If this is insufficient, subtrees are used, as in
+ *
+ *  (6: (0: 3@1000  3: 1@2000  4: 2@10)  12: (6: 3@8000  9: 1@9000  10: 2@20))
+ *
+ * The label on a subtree is the first address *after* that tree.  The
+ * subtrees are always anodes.  The label:subtree pairs require only
+ * two words each, so non-leaf subtrees have a different format; there
+ * is room for 12 label:subtree pairs in an fnode, or 60 in an anode.
+ *
+ * Within a directory, each dnode contains a pointer up to its parent
+ * dnode.  The root dnode points up to the directory's fnode.
+ *
+ * Each fnode contains a pointer to the directory that contains it
+ * (to the fnode of the directory).  So this pointer in a directory
+ * fnode is "..".
+ *
+ * On the disk, dnodes are all together in the center of the partition,
+ * and HPFS even manages to put all the dnodes for a single directory
+ * together, generally.  fnodes are out with the data.  anodes are seldom
+ * seen -- in fact noncontiguous files are seldom seen.  I think this is
+ * partly the open() call that lets programs specify the length of an
+ * output file when they know it, and partly because HPFS.IFS really is
+ * very good at resisting fragmentation. 
+ */
+
+/* notation */
+
+#define little_ushort(x) (*(unsigned short *) &(x))
+typedef void nonconst;
+
+/* super block ops */
+
+static void hpfs_read_inode(struct inode *);
+static void hpfs_put_super(struct super_block *);
+static void hpfs_statfs(struct super_block *, struct statfs *);
+static int hpfs_remount_fs(struct super_block *, int *);
+
+static const struct super_operations hpfs_sops =
+{
+	hpfs_read_inode,		/* read_inode */
+	NULL,				/* notify_change */
+	NULL,				/* write_inode */
+	NULL,				/* put_inode */
+	hpfs_put_super,			/* put_super */
+	NULL,				/* write_super */
+	hpfs_statfs,			/* statfs */
+	hpfs_remount_fs,		/* remount_fs */
+};
+
+/* file ops */
+
+static int hpfs_file_read(struct inode *, struct file *, char *, int);
+static secno hpfs_bmap(struct inode *, unsigned);
+
+static const struct file_operations hpfs_file_ops =
+{
+	NULL,				/* lseek - default */
+	hpfs_file_read,			/* read */
+	NULL,				/* write */
+	NULL,				/* readdir - bad */
+	NULL,				/* select - default */
+	NULL,				/* ioctl - default */
+	generic_mmap,			/* mmap */
+	NULL,				/* no special open is needed */
+	NULL,				/* release */
+	file_fsync,			/* fsync */
+};
+
+static const struct inode_operations hpfs_file_iops =
+{
+	(nonconst *) & hpfs_file_ops,	/* 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 */
+	(int (*)(struct inode *, int))
+	&hpfs_bmap,			/* bmap */
+	NULL,				/* truncate */
+	NULL,				/* permission */
+};
+
+/* directory ops */
+
+static int hpfs_dir_read(struct inode *inode, struct file *filp,
+			 char *buf, int count);
+static int hpfs_readdir(struct inode *inode, struct file *filp,
+			struct dirent *dirent, int count);
+static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
+
+static const struct file_operations hpfs_dir_ops =
+{
+	NULL,				/* lseek - default */
+	hpfs_dir_read,			/* read */
+	NULL,				/* write - bad */
+	hpfs_readdir,			/* readdir */
+	NULL,				/* select - default */
+	NULL,				/* ioctl - default */
+	NULL,				/* mmap */
+	NULL,				/* no special open code */
+	NULL,				/* no special release code */
+	file_fsync,			/* fsync */
+};
+
+static const struct inode_operations hpfs_dir_iops =
+{
+	(nonconst *) & hpfs_dir_ops,	/* default directory file ops */
+	NULL,				/* create */
+	hpfs_lookup,			/* lookup */
+	NULL,				/* link */
+	NULL,				/* unlink */
+	NULL,				/* symlink */
+	NULL,				/* mkdir */
+	NULL,				/* rmdir */
+	NULL,				/* mknod */
+	NULL,				/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL,				/* permission */
+};
+
+/* Four 512-byte buffers and the 2k block obtained by concatenating them */
+
+struct quad_buffer_head {
+	struct buffer_head *bh[4];
+	void *data;
+};
+
+/* forwards */
+
+static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+		      int *lowercase, int *conv);
+static int check_warn(int not_ok,
+		      const char *p1, const char *p2, const char *p3);
+static int zerop(void *addr, unsigned len);
+static void count_dnodes(struct inode *inode, dnode_secno dno,
+			 unsigned *n_dnodes, unsigned *n_subdirs);
+static unsigned count_bitmap(struct super_block *s);
+static unsigned count_one_bitmap(dev_t dev, secno secno);
+static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
+			  secno file_secno, struct buffer_head **bhp);
+static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
+				    const unsigned char *name, unsigned len,
+				      struct quad_buffer_head *qbh);
+static struct hpfs_dirent *map_pos_dirent(struct inode *inode, off_t *posp,
+					  struct quad_buffer_head *qbh);
+static void write_one_dirent(struct dirent *dirent, const unsigned char *name,
+			     unsigned namelen, ino_t ino, int lowercase);
+static dnode_secno dir_subdno(struct inode *inode, unsigned pos);
+static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
+					  int n,
+					  struct quad_buffer_head *qbh);
+static unsigned choose_conv(unsigned char *p, unsigned len);
+static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
+			     unsigned len);
+static dnode_secno fnode_dno(dev_t dev, ino_t ino);
+static struct fnode *map_fnode(dev_t dev, ino_t ino,
+			       struct buffer_head **bhp);
+static struct anode *map_anode(dev_t dev, unsigned secno,
+			       struct buffer_head **bhp);
+static struct dnode *map_dnode(dev_t dev, unsigned secno,
+			       struct quad_buffer_head *qbh);
+static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp);
+static void *map_4sectors(dev_t dev, unsigned secno,
+			  struct quad_buffer_head *qbh);
+static void brelse4(struct quad_buffer_head *qbh);
+
+/*
+ * make inode number for a file
+ */
+
+static inline ino_t file_ino(fnode_secno secno)
+{
+	return secno << 1 | 1;
+}
+
+/*
+ * make inode number for a directory
+ */
+
+static inline ino_t dir_ino(fnode_secno secno)
+{
+	return secno << 1;
+}
+
+/*
+ * get fnode address from an inode number
+ */
+
+static inline fnode_secno ino_secno(ino_t ino)
+{
+	return ino >> 1;
+}
+
+/*
+ * test for directory's inode number 
+ */
+
+static inline int ino_is_dir(ino_t ino)
+{
+	return (ino & 1) == 0;
+}
+
+/*
+ * conv= options
+ */
+
+#define CONV_BINARY 0			/* no conversion */
+#define CONV_TEXT 1			/* crlf->newline */
+#define CONV_AUTO 2			/* decide based on file contents */
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+static inline time_t local_to_gmt(time_t t)
+{
+	extern struct timezone sys_tz;
+	return t + sys_tz.tz_minuteswest * 60;
+}
+
+/* super block ops */
+
+/*
+ * mount.  This gets one thing, the root directory inode.  It does a
+ * bunch of guessed-at consistency checks.
+ */
+
+struct super_block *hpfs_read_super(struct super_block *s,
+				    void *options, int silent)
+{
+	struct hpfs_boot_block *bootblock;
+	struct hpfs_super_block *superblock;
+	struct hpfs_spare_block *spareblock;
+	struct hpfs_dirent *de;
+	struct buffer_head *bh0, *bh1, *bh2;
+	struct quad_buffer_head qbh;
+	dnode_secno root_dno;
+	dev_t dev;
+	uid_t uid;
+	gid_t gid;
+	umode_t umask;
+	int lowercase;
+	int conv;
+	int dubious;
+
+	/*
+	 * Get the mount options
+	 */
+
+	if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv)) {
+		printk("HPFS: syntax error in mount options.  Not mounted.\n");
+		s->s_dev = 0;
+		return 0;
+	}
+
+	/*
+	 * Fill in the super block struct
+	 */
+
+	lock_super(s);
+	dev = s->s_dev;
+	set_blocksize(dev, 512);
+
+	/*
+	 * fetch sectors 0, 16, 17
+	 */
+
+	bootblock = map_sector(dev, 0, &bh0);
+	if (!bootblock)
+		goto bail;
+
+	superblock = map_sector(dev, 16, &bh1);
+	if (!superblock)
+		goto bail0;
+
+	spareblock = map_sector(dev, 17, &bh2);
+	if (!spareblock)
+		goto bail1;
+
+	/*
+	 * Check that this fs looks enough like a known one that we can find
+	 * and read the root directory.
+	 */
+
+	if (bootblock->magic != 0xaa55
+	    || superblock->magic != SB_MAGIC
+	    || spareblock->magic != SP_MAGIC
+	    || bootblock->sig_28h != 0x28
+	    || memcmp(&bootblock->sig_hpfs, "HPFS    ", 8)
+	    || little_ushort(bootblock->bytes_per_sector) != 512) {
+		printk("HPFS: hpfs_read_super: Not HPFS\n");
+		goto bail2;
+	}
+
+	/*
+	 * Check for inconsistencies -- possibly wrong guesses here, possibly
+	 * filesystem problems.
+	 */
+
+	dubious = 0;
+
+	dubious |= check_warn(spareblock->dirty != 0,
+		       "`Improperly stopped'", "flag is set", "run CHKDSK");
+	dubious |= check_warn(spareblock->n_spares_used != 0,
+			      "Spare blocks", "may be in use", "run CHKDSK");
+
+	/*
+	 * Above errors mean we could get wrong answers if we proceed,
+	 * so don't
+	 */
+
+	if (dubious)
+		goto bail2;
+
+	dubious |= check_warn((spareblock->n_dnode_spares !=
+			       spareblock->n_dnode_spares_free),
+			      "Spare dnodes", "may be in use", "run CHKDSK");
+	dubious |= check_warn(superblock->zero1 != 0,
+			      "#1", "unknown word nonzero", "investigate");
+	dubious |= check_warn(superblock->zero3 != 0,
+			      "#3", "unknown word nonzero", "investigate");
+	dubious |= check_warn(superblock->zero4 != 0,
+			      "#4", "unknown word nonzero", "investigate");
+	dubious |= check_warn(!zerop(superblock->zero5,
+				     sizeof superblock->zero5),
+			      "#5", "unknown word nonzero", "investigate");
+	dubious |= check_warn(!zerop(superblock->zero6,
+				     sizeof superblock->zero6),
+			      "#6", "unknown word nonzero", "investigate");
+
+	if (dubious)
+		printk("HPFS: Proceeding, but operation may be unreliable\n");
+
+	/*
+	 * set fs read only
+	 */
+
+	s->s_flags |= MS_RDONLY;
+
+	/*
+	 * fill in standard stuff
+	 */
+
+	s->s_magic = HPFS_SUPER_MAGIC;
+	s->s_blocksize = 512;
+	s->s_blocksize_bits = 9;
+	s->s_op = (struct super_operations *) &hpfs_sops;
+
+	/*
+	 * fill in hpfs stuff
+	 */
+
+	s->s_hpfs_root = dir_ino(superblock->root);
+	s->s_hpfs_fs_size = superblock->n_sectors;
+	s->s_hpfs_dirband_size = superblock->n_dir_band / 4;
+	s->s_hpfs_dmap = superblock->dir_band_bitmap;
+	s->s_hpfs_bitmaps = superblock->bitmaps;
+	s->s_hpfs_uid = uid;
+	s->s_hpfs_gid = gid;
+	s->s_hpfs_mode = 0777 & ~umask;
+	s->s_hpfs_n_free = -1;
+	s->s_hpfs_n_free_dnodes = -1;
+	s->s_hpfs_lowercase = lowercase;
+	s->s_hpfs_conv = conv;
+
+	/*
+	 * done with the low blocks
+	 */
+
+	brelse(bh2);
+	brelse(bh1);
+	brelse(bh0);
+
+	/*
+	 * all set.  try it out.
+	 */
+
+	s->s_mounted = iget(s, s->s_hpfs_root);
+	unlock_super(s);
+
+	if (!s->s_mounted) {
+		printk("HPFS: hpfs_read_super: inode get failed\n");
+		s->s_dev = 0;
+		return 0;
+	}
+
+	/*
+	 * find the root directory's . pointer & finish filling in the inode
+	 */
+
+	root_dno = fnode_dno(dev, s->s_hpfs_root);
+	if (root_dno)
+		de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
+	if (!root_dno || !de) {
+		printk("HPFS: "
+		       "hpfs_read_super: root dir isn't in the root dir\n");
+		s->s_dev = 0;
+		return 0;
+	}
+
+	s->s_mounted->i_atime = local_to_gmt(de->read_date);
+	s->s_mounted->i_mtime = local_to_gmt(de->write_date);
+	s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
+
+	brelse4(&qbh);
+	return s;
+
+ bail2:
+	brelse(bh2);
+ bail1:
+	brelse(bh1);
+ bail0:
+	brelse(bh0);
+ bail:
+	s->s_dev = 0;
+	unlock_super(s);
+	return 0;
+}
+
+static int check_warn(int not_ok,
+		      const char *p1, const char *p2, const char *p3)
+{
+	if (not_ok)
+		printk("HPFS: %s %s. Please %s\n", p1, p2, p3);
+	return not_ok;
+}
+
+static int zerop(void *addr, unsigned len)
+{
+	unsigned char *p = addr;
+	return p[0] == 0 && memcmp(p, p + 1, len - 1) == 0;
+}
+
+/*
+ * A tiny parser for option strings, stolen from dosfs.
+ */
+
+static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+		      int *lowercase, int *conv)
+{
+	char *p, *rhs;
+
+	*uid = current->uid;
+	*gid = current->gid;
+	*umask = current->umask;
+	*lowercase = 1;
+	*conv = CONV_BINARY;
+
+	if (!opts)
+		return 1;
+
+	for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
+		if ((rhs = strchr(p, '=')) != 0)
+			*rhs++ = '\0';
+		if (!strcmp(p, "uid")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*uid = simple_strtoul(rhs, &rhs, 0);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "gid")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*gid = simple_strtoul(rhs, &rhs, 0);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "umask")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*umask = simple_strtoul(rhs, &rhs, 8);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "case")) {
+			if (!strcmp(rhs, "lower"))
+				*lowercase = 1;
+			else if (!strcmp(rhs, "asis"))
+				*lowercase = 0;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "conv")) {
+			if (!strcmp(rhs, "binary"))
+				*conv = CONV_BINARY;
+			else if (!strcmp(rhs, "text"))
+				*conv = CONV_TEXT;
+			else if (!strcmp(rhs, "auto"))
+				*conv = CONV_AUTO;
+			else
+				return 0;
+		}
+		else
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * read_inode.  This is called with exclusive access to a new inode that
+ * has only (i_dev,i_ino) set.  It is responsible for filling in the rest.
+ * We leave the dates blank, to be filled in from the dir entry.
+ *
+ * NOTE that there must be no sleeping from the return in this routine
+ * until lookup() finishes filling in the inode, otherwise the partly
+ * completed inode would be visible during the sleep.
+ *
+ * It is done in this strange and sinful way because the alternative
+ * is to read the fnode, find the dir pointer in it, read that fnode
+ * to get the dnode pointer, search through that whole directory for
+ * the ino we're reading, and get the dates.  It works that way, but
+ * ls sounds like fsck.
+ */
+
+static void hpfs_read_inode(struct inode *inode)
+{
+	struct super_block *s = inode->i_sb;
+
+	/* be ready to bail out */
+
+	inode->i_op = 0;
+	inode->i_mode = 0;
+
+	if (inode->i_ino == 0
+	    || ino_secno(inode->i_ino) >= inode->i_sb->s_hpfs_fs_size) {
+		printk("HPFS: read_inode: bad ino\n");
+		return;
+	}
+
+	/*
+	 * canned stuff
+	 */
+
+	inode->i_uid = s->s_hpfs_uid;
+	inode->i_gid = s->s_hpfs_gid;
+	inode->i_mode = s->s_hpfs_mode;
+	inode->i_hpfs_conv = s->s_hpfs_conv;
+
+	inode->i_hpfs_dno = 0;
+	inode->i_hpfs_n_secs = 0;
+	inode->i_hpfs_file_sec = 0;
+	inode->i_hpfs_disk_sec = 0;
+	inode->i_hpfs_dpos = 0;
+	inode->i_hpfs_dsubdno = 0;
+
+	/*
+	 * figure out whether we are looking at a directory or a file
+	 */
+
+	if (ino_is_dir(inode->i_ino))
+		inode->i_mode |= S_IFDIR;
+	else {
+		inode->i_mode |= S_IFREG;
+		inode->i_mode &= ~0111;
+	}
+
+	/*
+	 * these fields must be filled in from the dir entry, which we don't
+	 * have but lookup does.  It will fill them in before letting the
+	 * inode out of its grasp.
+	 */
+
+	inode->i_atime = 0;
+	inode->i_mtime = 0;
+	inode->i_ctime = 0;
+	inode->i_size = 0;
+
+	/*
+	 * fill in the rest
+	 */
+
+	if (S_ISREG(inode->i_mode)) {
+
+		inode->i_op = (struct inode_operations *) &hpfs_file_iops;
+		inode->i_nlink = 1;
+		inode->i_blksize = 512;
+
+	}
+	else {
+		unsigned n_dnodes, n_subdirs;
+		struct buffer_head *bh0;
+		struct fnode *fnode = map_fnode(inode->i_dev,
+						inode->i_ino, &bh0);
+
+		if (!fnode) {
+			printk("HPFS: read_inode: no fnode\n");
+			inode->i_mode = 0;
+			return;
+		}
+
+		inode->i_hpfs_parent_dir = dir_ino(fnode->up);
+		inode->i_hpfs_dno = fnode->u.external[0].disk_secno;
+
+		brelse(bh0);
+
+		n_dnodes = n_subdirs = 0;
+		count_dnodes(inode, inode->i_hpfs_dno, &n_dnodes, &n_subdirs);
+
+		inode->i_op = (struct inode_operations *) &hpfs_dir_iops;
+		inode->i_blksize = 512;	/* 2048 here confuses ls & du & ... */
+		inode->i_blocks = 4 * n_dnodes;
+		inode->i_size = 512 * inode->i_blocks;
+		inode->i_nlink = 2 + n_subdirs;
+	}
+}
+
+/*
+ * unmount.
+ */
+
+static void hpfs_put_super(struct super_block *s)
+{
+	lock_super(s);
+	s->s_dev = 0;
+	unlock_super(s);
+}
+
+/*
+ * statfs.  For free inode counts we report the count of dnodes in the
+ * directory band -- not exactly right but pretty analagous.
+ */
+
+static void hpfs_statfs(struct super_block *s, struct statfs *buf)
+{
+	/*
+	 * count the bits in the bitmaps, unless we already have
+	 */
+
+	if (s->s_hpfs_n_free == -1) {
+		s->s_hpfs_n_free = count_bitmap(s);
+		s->s_hpfs_n_free_dnodes =
+		    count_one_bitmap(s->s_dev, s->s_hpfs_dmap);
+	}
+
+	/*
+	 * fill in the user statfs struct
+	 */
+
+	put_fs_long(s->s_magic, &buf->f_type);
+	put_fs_long(512, &buf->f_bsize);
+	put_fs_long(s->s_hpfs_fs_size, &buf->f_blocks);
+	put_fs_long(s->s_hpfs_n_free, &buf->f_bfree);
+	put_fs_long(s->s_hpfs_n_free, &buf->f_bavail);
+	put_fs_long(s->s_hpfs_dirband_size, &buf->f_files);
+	put_fs_long(s->s_hpfs_n_free_dnodes, &buf->f_ffree);
+	put_fs_long(254, &buf->f_namelen);
+}
+
+/*
+ * remount.  Don't let read only be turned off.
+ */
+
+static int hpfs_remount_fs(struct super_block *s, int *flags)
+{
+	if (!(*flags & MS_RDONLY))
+		return -EINVAL;
+	return 0;
+}
+
+/*
+ * count the dnodes in a directory, and the subdirs.
+ */
+
+static void count_dnodes(struct inode *inode, dnode_secno dno,
+			 unsigned *n_dnodes, unsigned *n_subdirs)
+{
+	struct quad_buffer_head qbh;
+	struct dnode *dnode;
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end;
+
+	dnode = map_dnode(inode->i_dev, dno, &qbh);
+	if (!dnode)
+		return;
+	de = dnode_first_de(dnode);
+	de_end = dnode_end_de(dnode);
+
+	(*n_dnodes)++;
+
+	for (; de < de_end; de = de_next_de(de)) {
+		if (de->down)
+			count_dnodes(inode, de_down_pointer(de),
+				     n_dnodes, n_subdirs);
+		if (de->directory && !de->first)
+			(*n_subdirs)++;
+		if (de->last || de->length == 0)
+			break;
+	}
+
+	brelse4(&qbh);
+}
+
+/*
+ * count the bits in the free space bit maps
+ */
+
+static unsigned count_bitmap(struct super_block *s)
+{
+	unsigned n, count, n_bands;
+	secno *bitmaps;
+	struct quad_buffer_head qbh;
+
+	/*
+	 * there is one bit map for each 16384 sectors
+	 */
+	n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+
+	/*
+	 * their locations are given in an array pointed to by the super
+	 * block
+	 */
+	bitmaps = map_4sectors(s->s_dev, s->s_hpfs_bitmaps, &qbh);
+	if (!bitmaps)
+		return 0;
+
+	count = 0;
+
+	/*
+	 * map each one and count the free sectors
+	 */
+	for (n = 0; n < n_bands; n++)
+		if (bitmaps[n] == 0)
+			printk("HPFS: bit map pointer missing\n");
+		else
+			count += count_one_bitmap(s->s_dev, bitmaps[n]);
+
+	brelse4(&qbh);
+	return count;
+}
+
+/*
+ * Read in one bit map, count the bits, return the count.
+ */
+
+static unsigned count_one_bitmap(dev_t dev, secno secno)
+{
+	struct quad_buffer_head qbh;
+	char *bits;
+	unsigned i, count;
+
+	bits = map_4sectors(dev, secno, &qbh);
+	if (!bits)
+		return 0;
+
+	count = 0;
+
+	for (i = 0; i < 8 * 2048; i++)
+		count += (test_bit(i, bits) != 0);
+	brelse4(&qbh);
+
+	return count;
+}
+
+/* file ops */
+
+/*
+ * read.  Read the bytes, put them in buf, return the count.
+ */
+
+static int hpfs_file_read(struct inode *inode, struct file *filp,
+			  char *buf, int count)
+{
+	unsigned q, r, n, n0;
+	struct buffer_head *bh;
+	char *block;
+	char *start;
+
+	if (inode == 0 || !S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	/*
+	 * truncate count at EOF
+	 */
+	if (count > inode->i_size - filp->f_pos)
+		count = inode->i_size - filp->f_pos;
+
+	start = buf;
+	while (count > 0) {
+		/*
+		 * get file sector number, offset in sector, length to end of
+		 * sector
+		 */
+		q = filp->f_pos >> 9;
+		r = filp->f_pos & 511;
+		n = 512 - r;
+
+		/*
+		 * get length to copy to user buffer
+		 */
+		if (n > count)
+			n = count;
+
+		/*
+		 * read the sector, copy to user
+		 */
+		block = map_sector(inode->i_dev, hpfs_bmap(inode, q), &bh);
+		if (!block)
+			return -EIO;
+
+		/*
+		 * but first decide if it has \r\n, if the mount option said
+		 * to do that
+		 */
+		if (inode->i_hpfs_conv == CONV_AUTO)
+			inode->i_hpfs_conv = choose_conv(block + r, n);
+
+		if (inode->i_hpfs_conv == CONV_BINARY) {
+			/*
+			 * regular copy, output length is same as input
+			 * length
+			 */
+			memcpy_tofs(buf, block + r, n);
+			n0 = n;
+		}
+		else {
+			/*
+			 * squeeze out \r, output length varies
+			 */
+			n0 = convcpy_tofs(buf, block + r, n);
+			if (count > inode->i_size - filp->f_pos - n + n0)
+				count = inode->i_size - filp->f_pos - n + n0;
+		}
+
+		brelse(bh);
+
+		/*
+		 * advance input n bytes, output n0 bytes
+		 */
+		filp->f_pos += n;
+		buf += n0;
+		count -= n0;
+	}
+
+	return buf - start;
+}
+
+/*
+ * This routine implements conv=auto.  Return CONV_BINARY or CONV_TEXT.
+ */
+
+static unsigned choose_conv(unsigned char *p, unsigned len)
+{
+	unsigned tvote, bvote;
+	unsigned c;
+
+	tvote = bvote = 0;
+
+	while (len--) {
+		c = *p++;
+		if (c < ' ')
+			if (c == '\r' && len && *p == '\n')
+				tvote += 10;
+			else if (c == '\t' || c == '\n');
+			else
+				bvote += 5;
+		else if (c < '\177')
+			tvote++;
+		else
+			bvote += 5;
+	}
+
+	if (tvote > bvote)
+		return CONV_TEXT;
+	else
+		return CONV_BINARY;
+}
+
+/*
+ * This routine implements conv=text.  :s/crlf/nl/
+ */
+
+static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
+			     unsigned len)
+{
+	unsigned char *start = out;
+
+	while (len--) {
+		unsigned c = *in++;
+		if (c == '\r' && (len == 0 || *in == '\n'));
+		else
+			put_fs_byte(c, out++);
+	}
+
+	return out - start;
+}
+
+/*
+ * Return the disk sector number containing a file sector.
+ */
+
+static secno hpfs_bmap(struct inode *inode, unsigned file_secno)
+{
+	unsigned n, disk_secno;
+	struct fnode *fnode;
+	struct buffer_head *bh;
+
+	/*
+	 * There is one sector run cached in the inode. See if the sector is
+	 * in it.
+	 */
+
+	n = file_secno - inode->i_hpfs_file_sec;
+	if (n < inode->i_hpfs_n_secs)
+		return inode->i_hpfs_disk_sec + n;
+
+	/*
+	 * No, read the fnode and go find the sector.
+	 */
+
+	else {
+		fnode = map_fnode(inode->i_dev, inode->i_ino, &bh);
+		if (!fnode)
+			return 0;
+		disk_secno = bplus_lookup(inode, &fnode->btree,
+					  file_secno, &bh);
+		brelse(bh);
+		return disk_secno;
+	}
+}
+
+/*
+ * Search allocation tree *b for the given file sector number and return
+ * the disk sector number.  Buffer *bhp has the tree in it, and can be
+ * reused for subtrees when access to *b is no longer needed.
+ * *bhp is busy on entry and exit. 
+ */
+
+static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
+			  secno file_secno, struct buffer_head **bhp)
+{
+	int i;
+
+	/*
+	 * A leaf-level tree gives a list of sector runs.  Find the one
+	 * containing the file sector we want, cache the map info in the
+	 * inode for later, and return the corresponding disk sector.
+	 */
+
+	if (!b->internal) {
+		struct bplus_leaf_node *n = b->u.external;
+		for (i = 0; i < b->n_used_nodes; i++) {
+			unsigned t = file_secno - n[i].file_secno;
+			if (t < n[i].length) {
+				inode->i_hpfs_file_sec = n[i].file_secno;
+				inode->i_hpfs_disk_sec = n[i].disk_secno;
+				inode->i_hpfs_n_secs = n[i].length;
+				return n[i].disk_secno + t;
+			}
+		}
+	}
+
+	/*
+	 * A non-leaf tree gives a list of subtrees.  Find the one containing
+	 * the file sector we want, read it in, and recurse to search it.
+	 */
+
+	else {
+		struct bplus_internal_node *n = b->u.internal;
+		for (i = 0; i < b->n_used_nodes; i++) {
+			if (file_secno < n[i].file_secno) {
+				struct anode *anode;
+				anode_secno ano = n[i].down;
+				brelse(*bhp);
+				anode = map_anode(inode->i_dev, ano, bhp);
+				if (!anode)
+					break;
+				return bplus_lookup(inode, &anode->btree,
+						    file_secno, bhp);
+			}
+		}
+	}
+
+	/*
+	 * If we get here there was a hole in the file.  As far as I know we
+	 * never do get here, but falling off the end would be indelicate. So
+	 * return a pointer to a handy all-zero sector.  This is not a
+	 * reasonable way to handle files with holes if they really do
+	 * happen.
+	 */
+
+	printk("HPFS: bplus_lookup: sector not found\n");
+	return 15;
+}
+
+/* directory ops */
+
+/*
+ * lookup.  Search the specified directory for the specified name, set
+ * *result to the corresponding inode.
+ *
+ * lookup uses the inode number to tell read_inode whether it is reading
+ * the inode of a directory or a file -- file ino's are odd, directory
+ * ino's are even.  read_inode avoids i/o for file inodes; everything
+ * needed is up here in the directory.  (And file fnodes are out in
+ * the boondocks.)
+ */
+
+static int hpfs_lookup(struct inode *dir, const char *name, int len,
+		       struct inode **result)
+{
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	struct inode *inode;
+	ino_t ino;
+
+	/* In case of madness */
+
+	*result = 0;
+	if (dir == 0)
+		return -ENOENT;
+	if (!S_ISDIR(dir->i_mode))
+		goto bail;
+
+	/*
+	 * Read in the directory entry. "." is there under the name ^A^A .
+	 * Always read the dir even for . and .. in case we need the dates.
+	 */
+
+	if (name[0] == '.' && len == 1)
+		de = map_dirent(dir, dir->i_hpfs_dno, "\001\001", 2, &qbh);
+	else if (name[0] == '.' && name[1] == '.' && len == 2)
+		de = map_dirent(dir,
+				fnode_dno(dir->i_dev, dir->i_hpfs_parent_dir),
+				"\001\001", 2, &qbh);
+	else
+		de = map_dirent(dir, dir->i_hpfs_dno, name, len, &qbh);
+
+	/*
+	 * This is not really a bailout, just means file not found.
+	 */
+
+	if (!de)
+		goto bail;
+
+	/*
+	 * Get inode number, what we're after.
+	 */
+
+	if (de->directory)
+		ino = dir_ino(de->fnode);
+	else
+		ino = file_ino(de->fnode);
+
+	/*
+	 * Go find or make an inode.
+	 */
+
+	if (!(inode = iget(dir->i_sb, ino)))
+		goto bail1;
+
+	/*
+	 * Fill in the info from the directory if this is a newly created
+	 * inode.
+	 */
+
+	if (!inode->i_atime) {
+		inode->i_atime = local_to_gmt(de->read_date);
+		inode->i_mtime = local_to_gmt(de->write_date);
+		inode->i_ctime = local_to_gmt(de->creation_date);
+		if (de->read_only)
+			inode->i_mode &= ~0222;
+		if (!de->directory) {
+			inode->i_size = de->file_size;
+			/*
+			 * i_blocks should count the fnode and any anodes.
+			 * We count 1 for the fnode and don't bother about
+			 * anodes -- the disk heads are on the directory band
+			 * and we want them to stay there.
+			 */
+			inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
+		}
+	}
+
+	brelse4(&qbh);
+
+	/*
+	 * Made it.
+	 */
+
+	*result = inode;
+	iput(dir);
+	return 0;
+
+	/*
+	 * Didn't.
+	 */
+ bail1:
+	brelse4(&qbh);
+ bail:
+	iput(dir);
+	return -ENOENT;
+}
+
+/*
+ * Compare two counted strings ignoring case.
+ * HPFS directory order sorts letters as if they're upper case.
+ */
+
+static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2,
+			     unsigned n)
+{
+	int t;
+
+	if (n != 0)
+		do {
+			unsigned c1 = *s1++;
+			unsigned c2 = *s2++;
+			if (c1 - 'a' < 26)
+				c1 -= 040;
+			if (c2 - 'a' < 26)
+				c2 -= 040;
+			if ((t = c1 - c2) != 0)
+				return t;
+		} while (--n != 0);
+
+	return 0;
+}
+
+/*
+ * Search a directory for the given name, return a pointer to its dir entry
+ * and a pointer to the buffer containing it.
+ */
+
+static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
+				      const unsigned char *name, unsigned len,
+				      struct quad_buffer_head *qbh)
+{
+	struct dnode *dnode;
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end;
+	int t, l;
+
+	/*
+	 * read the dnode at the root of our subtree
+	 */
+	dnode = map_dnode(inode->i_dev, dno, qbh);
+	if (!dnode)
+		return 0;
+
+	/*
+	 * get pointers to start and end+1 of dir entries
+	 */
+	de = dnode_first_de(dnode);
+	de_end = dnode_end_de(dnode);
+
+	/*
+	 * look through the entries for the name we're after
+	 */
+	for ( ; de < de_end; de = de_next_de(de)) {
+
+		/*
+		 * compare names
+		 */
+		l = len < de->namelen ? len : de->namelen;
+		t = memcasecmp(name, de->name, l);
+
+		/*
+		 * initial substring matches, compare lengths
+		 */
+		if (t == 0) {
+			t = len - de->namelen;
+			/* bingo */
+			if (t == 0)
+				return de;
+		}
+
+		/*
+		 * wanted name .lt. dir name => not present.
+		 */
+		if (t < 0) {
+			/*
+			 * if there is a subtree, search it.
+			 */
+			if (de->down) {
+				dnode_secno sub_dno = de_down_pointer(de);
+				brelse4(qbh);
+				return map_dirent(inode, sub_dno,
+						  name, len, qbh);
+			}
+			else
+				break;
+		}
+
+		/*
+		 * de->last is set on the last name in the dnode (it's always
+		 * a "\377" pseudo entry).  de->length == 0 means we're about
+		 * to infinite loop. This test does nothing in a well-formed
+		 * dnode.
+		 */
+		if (de->last || de->length == 0)
+			break;
+	}
+
+	/*
+	 * name not found.
+	 */
+
+	return 0;
+}
+
+/*
+ * readdir.  Return exactly 1 dirent.  (I tried and tried, but currently
+ * the interface with libc just does not permit more than 1.  If it gets
+ * fixed, throw this out and just walk the tree and write records into
+ * the user buffer.)
+ *
+ * We keep track of our position in the dnode tree with a sort of
+ * dewey-decimal record of subtree locations.  Like so:
+ *
+ *   (1 (1.1 1.2 1.3) 2 3 (3.1 (3.1.1 3.1.2) 3.2 3.3 (3.3.1)) 4)
+ *
+ * Subtrees appear after their file, out of lexical order,
+ * which would be before their file.  It's easier.
+ *
+ * A directory can't hold more than 56 files, so 6 bits are used for
+ * position numbers.  If the tree is so deep that the position encoding
+ * doesn't fit, I'm sure something absolutely fascinating happens.
+ *
+ * The actual sequence of f_pos values is
+ *     0 => .   -1 => ..   1 1.1 ... 8.9 9 => files  -2 => eof
+ *
+ * The directory inode caches one position-to-dnode correspondence so
+ * we won't have to repeatedly scan the top levels of the tree. 
+ */
+
+static int hpfs_readdir(struct inode *inode, struct file *filp,
+			struct dirent *dirent, int likely_story)
+{
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	int namelen, lc;
+	ino_t ino;
+
+	if (inode == 0
+	    || inode->i_sb == 0
+	    || !S_ISDIR(inode->i_mode))
+		return -EBADF;
+
+	lc = inode->i_sb->s_hpfs_lowercase;
+
+	switch (filp->f_pos) {
+	case 0:
+		write_one_dirent(dirent, ".", 1, inode->i_ino, lc);
+		filp->f_pos = -1;
+		return 1;
+
+	case -1:
+		write_one_dirent(dirent, "..", 2,
+				 inode->i_hpfs_parent_dir, lc);
+		filp->f_pos = 1;
+		return 2;
+
+	case -2:
+		return 0;
+
+	default:
+		de = map_pos_dirent(inode, &filp->f_pos, &qbh);
+		if (!de) {
+			filp->f_pos = -2;
+			return 0;
+		}
+
+		namelen = de->namelen;
+		if (de->directory)
+			ino = dir_ino(de->fnode);
+		else
+			ino = file_ino(de->fnode);
+		write_one_dirent(dirent, de->name, namelen, ino, lc);
+		brelse4(&qbh);
+
+		return namelen;
+	}
+}
+
+/*
+ * Send the given name and ino off to the user dirent struct at *dirent.
+ * Blam it to lowercase if the mount option said to.
+ *
+ * Note that Linux d_reclen is the length of the file name, and has nothing
+ * to do with the length of the dirent record.
+ */
+
+static void write_one_dirent(struct dirent *dirent, const unsigned char *name,
+			     unsigned namelen, ino_t ino, int lowercase)
+{
+	unsigned n;
+
+	put_fs_long(ino, &dirent->d_ino);
+	put_fs_word(namelen, &dirent->d_reclen);
+
+	if (lowercase)
+		for (n = namelen; n != 0;) {
+			unsigned t = name[--n];
+			if (t - 'A' < 26)
+				t += 040;
+			put_fs_byte(t, &dirent->d_name[n]);
+		}
+	else
+		memcpy_tofs(dirent->d_name, name, namelen);
+
+	put_fs_byte(0, &dirent->d_name[namelen]);
+}
+
+/*
+ * Map the dir entry at subtree coordinates given by *posp, and
+ * increment *posp to point to the following dir entry. 
+ */
+
+static struct hpfs_dirent *map_pos_dirent(struct inode *inode, off_t *posp,
+					  struct quad_buffer_head *qbh)
+{
+	unsigned pos, q, r;
+	dnode_secno dno;
+	struct hpfs_dirent *de;
+
+	/*
+	 * Get the position code and split off the rightmost index r
+	 */
+
+	pos = *posp;
+	q = pos >> 6;
+	r = pos & 077;
+
+	/*
+	 * Get the sector address of the dnode
+	 * pointed to by the leading part q
+	 */
+
+	dno = dir_subdno(inode, q);
+	if (!dno)
+		return 0;
+
+	/*
+	 * Get the entry at index r in dnode q
+	 */
+
+	de = map_nth_dirent(inode->i_dev, dno, r, qbh);
+
+	/*
+	 * If none, we're out of files in this dnode.  Ascend.
+	 */
+
+	if (!de) {
+		if (q == 0)
+			return 0;
+		*posp = q + 1;
+		return map_pos_dirent(inode, posp, qbh);
+	}
+
+	/*
+	 * If a subtree is here, descend.
+	 */
+
+	if (de->down)
+		*posp = pos << 6 | 1;
+	else
+		*posp = pos + 1;
+
+	/*
+	 * Don't return the ^A^A and \377 entries.
+	 */
+
+	if (de->first || de->last) {
+		brelse4(qbh);
+		return map_pos_dirent(inode, posp, qbh);
+	}
+	else
+		return de;
+}
+
+/*
+ * Return the address of the dnode with subtree coordinates given by pos.
+ */
+
+static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
+{
+	struct hpfs_dirent *de;
+	struct quad_buffer_head qbh;
+
+	/*
+	 * 0 is the root dnode
+	 */
+
+	if (pos == 0)
+		return inode->i_hpfs_dno;
+
+	/*
+	 * we have one pos->dnode translation cached in the inode
+	 */
+
+	else if (pos == inode->i_hpfs_dpos)
+		return inode->i_hpfs_dsubdno;
+
+	/*
+	 * otherwise go look
+	 */
+
+	else {
+		unsigned q = pos >> 6;
+		unsigned r = pos & 077;
+		dnode_secno dno;
+
+		/*
+		 * dnode at position q
+		 */
+		dno = dir_subdno(inode, q);
+		if (dno == 0)
+			return 0;
+
+		/*
+		 * entry at index r
+		 */
+		de = map_nth_dirent(inode->i_dev, dno, r, &qbh);
+		if (!de || !de->down)
+			return 0;
+
+		/*
+		 * get the dnode down pointer
+		 */
+		dno = de_down_pointer(de);
+		brelse4(&qbh);
+
+		/*
+		 * cache it for next time
+		 */
+		inode->i_hpfs_dpos = pos;
+		inode->i_hpfs_dsubdno = dno;
+		return dno;
+	}
+}
+
+/*
+ * Return the dir entry at index n in dnode dno, or 0 if there isn't one
+ */
+
+static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
+					  int n,
+					  struct quad_buffer_head *qbh)
+{
+	int i;
+	struct hpfs_dirent *de, *de_end;
+	struct dnode *dnode = map_dnode(dev, dno, qbh);
+
+	de = dnode_first_de(dnode);
+	de_end = dnode_end_de(dnode);
+
+	for (i = 1; de < de_end; i++, de = de_next_de(de)) {
+		if (i == n)
+			return de;
+		if (de->last || de->length == 0)
+			break;
+	}
+
+	brelse4(qbh);
+	return 0;
+}
+
+static int hpfs_dir_read(struct inode *inode, struct file *filp,
+			 char *buf, int count)
+{
+	return -EISDIR;
+}
+
+/* Return the dnode pointer in a directory fnode */
+
+static dnode_secno fnode_dno(dev_t dev, ino_t ino)
+{
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	dnode_secno dno;
+
+	fnode = map_fnode(dev, ino, &bh);
+	if (!fnode)
+		return 0;
+
+	dno = fnode->u.external[0].disk_secno;
+	brelse(bh);
+	return dno;
+}
+
+/* Map an fnode into a buffer and return pointers to it and to the buffer. */
+
+static struct fnode *map_fnode(dev_t dev, ino_t ino, struct buffer_head **bhp)
+{
+	struct fnode *fnode;
+
+	if (ino == 0) {
+		printk("HPFS: missing fnode\n");
+		return 0;
+	}
+
+	fnode = map_sector(dev, ino_secno(ino), bhp);
+	if (fnode)
+		if (fnode->magic != FNODE_MAGIC) {
+			printk("HPFS: map_fnode: bad fnode pointer\n");
+			brelse(*bhp);
+			return 0;
+		}
+	return fnode;
+}
+
+/* Map an anode into a buffer and return pointers to it and to the buffer. */
+
+static struct anode *map_anode(dev_t dev, unsigned secno,
+			       struct buffer_head **bhp)
+{
+	struct anode *anode;
+
+	if (secno == 0) {
+		printk("HPFS: missing anode\n");
+		return 0;
+	}
+
+	anode = map_sector(dev, secno, bhp);
+	if (anode)
+		if (anode->magic != ANODE_MAGIC || anode->self != secno) {
+			printk("HPFS: map_anode: bad anode pointer\n");
+			brelse(*bhp);
+			return 0;
+		}
+	return anode;
+}
+
+/* Map a dnode into a buffer and return pointers to it and to the buffer. */
+
+static struct dnode *map_dnode(dev_t dev, unsigned secno,
+			       struct quad_buffer_head *qbh)
+{
+	struct dnode *dnode;
+
+	if (secno == 0) {
+		printk("HPFS: missing dnode\n");
+		return 0;
+	}
+
+	dnode = map_4sectors(dev, secno, qbh);
+	if (dnode)
+		if (dnode->magic != DNODE_MAGIC || dnode->self != secno) {
+			printk("HPFS: map_dnode: bad dnode pointer\n");
+			brelse4(qbh);
+			return 0;
+		}
+	return dnode;
+}
+
+/* Map a sector into a buffer and return pointers to it and to the buffer. */
+
+static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp)
+{
+	struct buffer_head *bh;
+
+	if ((*bhp = bh = bread(dev, secno, 512)) != 0)
+		return bh->b_data;
+	else {
+		printk("HPFS: map_sector: read error\n");
+		return 0;
+	}
+}
+
+/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+
+static void *map_4sectors(dev_t dev, unsigned secno,
+			  struct quad_buffer_head *qbh)
+{
+	struct buffer_head *bh;
+	char *data;
+
+	if (secno & 3) {
+		printk("HPFS: map_4sectors: unaligned read\n");
+		return 0;
+	}
+
+	qbh->data = data = kmalloc(2048, GFP_KERNEL);
+	if (!data)
+		goto bail;
+
+	qbh->bh[0] = bh = breada(dev,
+				 secno, secno + 1, secno + 2, secno + 3, -1);
+	if (!bh)
+		goto bail0;
+	memcpy(data, bh->b_data, 512);
+
+	qbh->bh[1] = bh = bread(dev, secno + 1, 512);
+	if (!bh)
+		goto bail1;
+	memcpy(data + 512, bh->b_data, 512);
+
+	qbh->bh[2] = bh = bread(dev, secno + 2, 512);
+	if (!bh)
+		goto bail2;
+	memcpy(data + 2 * 512, bh->b_data, 512);
+
+	qbh->bh[3] = bh = bread(dev, secno + 3, 512);
+	if (!bh)
+		goto bail3;
+	memcpy(data + 3 * 512, bh->b_data, 512);
+
+	return data;
+
+ bail3:
+	brelse(qbh->bh[2]);
+ bail2:
+	brelse(qbh->bh[1]);
+ bail1:
+	brelse(qbh->bh[0]);
+ bail0:
+	kfree_s(data, 2048);
+ bail:
+	printk("HPFS: map_4sectors: read error\n");
+	return 0;
+}
+
+/* Deallocate a 4-buffer block */
+
+static void brelse4(struct quad_buffer_head *qbh)
+{
+	brelse(qbh->bh[3]);
+	brelse(qbh->bh[2]);
+	brelse(qbh->bh[1]);
+	brelse(qbh->bh[0]);
+	kfree_s(qbh->data, 2048);
+}
diff --git a/fs/inode.c b/fs/inode.c
index 828bb41..db0425f 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -303,7 +303,7 @@
 	wait_on_inode(inode);
 	if (!inode->i_count) {
 		printk("VFS: iput: trying to free free inode\n");
-		printk("VFS: device %d/%d, inode %d, mode=0%07o\n",
+		printk("VFS: device %d/%d, inode %lu, mode=0%07o\n",
 			MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
 					inode->i_ino, inode->i_mode);
 		return;
@@ -441,27 +441,13 @@
 			nr_free_inodes--;
 		inode->i_count++;
 		if (crossmntp && inode->i_mount) {
-			int i;
-
-			for (i = 0 ; i<NR_SUPER ; i++)
-				if (super_blocks[i].s_covered==inode)
-					break;
-			if (i >= NR_SUPER) {
-				printk("VFS: Mounted inode hasn't got sb\n");
-				if (empty)
-					iput(empty);
-				return inode;
-			}
+			struct inode * tmp = inode->i_mount;
 			iput(inode);
-			if (!(inode = super_blocks[i].s_mounted))
-				printk("VFS: Mounted device %d/%d has no rootinode\n",
-					MAJOR(inode->i_dev), MINOR(inode->i_dev));
-			else {
-				if (!inode->i_count)
-					nr_free_inodes--;
-				inode->i_count++;
-				wait_on_inode(inode);
-			}
+			inode = tmp;
+			if (!inode->i_count)
+				nr_free_inodes--;
+			inode->i_count++;
+			wait_on_inode(inode);
 		}
 		if (empty)
 			iput(empty);
diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile
index 1417145..a780af4 100644
--- a/fs/isofs/Makefile
+++ b/fs/isofs/Makefile
@@ -19,9 +19,6 @@
 isofs.o: $(OBJS)
 	$(LD) -r -o isofs.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index 829beec..6a78e08 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -202,15 +202,12 @@
 		printk("Nchar: %d\n",i);
 #endif
 
-		if (i) {
-		        while (cache.lock);
-		        cache.lock = 1;
+		if (i && i+1 < sizeof(cache.filename)) {
 			cache.ino = inode_number;
 			cache.dir = inode->i_ino;
 			cache.dev = inode->i_dev;
 			strncpy(cache.filename, dpnt, i);
 			cache.dlen = dlen;
-			cache.lock = 0;
 		      };
 
 		if (rrflag) kfree(dpnt);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index e5457e2..505e0d5 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -5,19 +5,21 @@
  *
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
+
 #include <linux/config.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
 #include <linux/iso_fs.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/malloc.h>
+#include <linux/errno.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
-#include <linux/errno.h>
 
 #if defined(CONFIG_BLK_DEV_SR)
 extern int check_cdrom_media_change(int, int);
@@ -239,9 +241,11 @@
 	
 	brelse(bh);
 	
-	printk("Max size:%d   Log zone size:%d\n",s->u.isofs_sb.s_max_size, 
+	printk("Max size:%ld   Log zone size:%ld\n",
+	       s->u.isofs_sb.s_max_size, 
 	       s->u.isofs_sb.s_log_zone_size);
-	printk("First datazone:%d   Root inode number %d\n",s->u.isofs_sb.s_firstdatazone,
+	printk("First datazone:%ld   Root inode number %d\n",
+	       s->u.isofs_sb.s_firstdatazone,
 	       isonum_733 (rootp->extent) << ISOFS_BLOCK_BITS);
 	if(high_sierra) printk("Disc in High Sierra format.\n");
 	unlock_super(s);
@@ -263,26 +267,26 @@
 		printk("get root inode failed\n");
 		return NULL;
 	}
-#if defined(CONFIG_BLK_DEV_SR)
-	if(MAJOR(s->s_dev) == 11) {
+#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
+	if (MAJOR(s->s_dev) == SCSI_CDROM_MAJOR) {
 		/* Check this one more time. */
 		if(check_cdrom_media_change(s->s_dev, 0))
 		  goto out;
-	};
+	}
 #endif
 #if defined(CONFIG_CDU31A)
-	if(MAJOR(s->s_dev) == 15) {
+	if (MAJOR(s->s_dev) == CDU31A_CDROM_MAJOR) {
 		/* Check this one more time. */
 		if(check_cdu31a_media_change(s->s_dev, 0))
 		  goto out;
-	};
+	}
 #endif
 #if defined(CONFIG_MCD)
-	if(MAJOR(s->s_dev) == 23) {
+	if (MAJOR(s->s_dev) == MITSUMI_CDROM_MAJOR) {
 		/* Check this one more time. */
 		if(check_mcd_media_change(s->s_dev, 0))
 		  goto out;
-	};
+	}
 #endif
 	return s;
  out: /* Kick out for various error conditions */
@@ -400,7 +404,7 @@
 	/* I have no idea what extended attributes are used for, so
 	   we will flag it for now */
 	if(raw_inode->ext_attr_length[0] != 0){
-		printk("Extended attributes present for ISO file (%d).\n",
+		printk("Extended attributes present for ISO file (%ld).\n",
 		       inode->i_ino);
 	}
 #endif
@@ -408,13 +412,13 @@
 	/* I have no idea what file_unit_size is used for, so
 	   we will flag it for now */
 	if(raw_inode->file_unit_size[0] != 0){
-		printk("File unit size != 0 for ISO file.(%d)\n",inode->i_ino);
+		printk("File unit size != 0 for ISO file (%ld).\n",inode->i_ino);
 	}
 
 	/* I have no idea what other flag bits are used for, so
 	   we will flag it for now */
 	if((raw_inode->flags[-high_sierra] & ~2)!= 0){
-		printk("Unusual flag settings for ISO file.(%d %x)\n",
+		printk("Unusual flag settings for ISO file (%ld %x).\n",
 		       inode->i_ino, raw_inode->flags[-high_sierra]);
 	}
 
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
index 24fd8c9..fc7cc24 100644
--- a/fs/isofs/namei.c
+++ b/fs/isofs/namei.c
@@ -224,8 +224,6 @@
 	}
 
 	ino = 0;
-	while(cache.lock);
-	cache.lock = 1;
 	if (dir->i_dev == cache.dev && 
 	    dir->i_ino == cache.dir &&
 	    len == cache.dlen && 
@@ -239,7 +237,6 @@
 	    if (cache.dlen == 2 && cache.filename[0] == '.' && 
 		cache.filename[1] == '.') ino = 0;
 	  };
-	cache.lock = 0;
 
 	if (!ino) {
 	  if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))) {
diff --git a/fs/isofs/util.c b/fs/isofs/util.c
index 141891d..dbeee86 100644
--- a/fs/isofs/util.c
+++ b/fs/isofs/util.c
@@ -5,7 +5,7 @@
  *  of the iso 9660 standard in which they are described.  isonum_733 will
  *  convert numbers according to section 7.3.3, etc.
  *
- *  isofs special functions.  This file was lifted in it's entirety from
+ *  isofs special functions.  This file was lifted in its entirety from
  * the bsd386 iso9660 filesystem, by Pace Williamson.
  */
 
diff --git a/fs/locks.c b/fs/locks.c
index aae91cc..80c4597 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -179,7 +179,7 @@
 	/* Find first lock owned by caller ... */
 
 	before = &filp->f_inode->i_flock;
-	while ((fl = *before) && task != fl->fl_owner && fd != fl->fl_fd)
+	while ((fl = *before) && (task != fl->fl_owner || fd != fl->fl_fd))
 		before = &fl->fl_next;
 
 	/* The list is sorted by owner and fd ... */
@@ -282,9 +282,9 @@
 	 */
 
 	before = &filp->f_inode->i_flock;
-	while (   (fl = *before)
-               && caller->fl_owner != fl->fl_owner
-               && caller->fl_fd != fl->fl_fd)
+	while ((fl = *before) &&
+	    (caller->fl_owner != fl->fl_owner ||
+	     caller->fl_fd != fl->fl_fd))
 		before = &fl->fl_next;
 
 	/*
@@ -366,7 +366,6 @@
 			fl->fl_start = caller->fl_start;
 			fl->fl_end   = caller->fl_end;
 			fl->fl_type  = caller->fl_type;
-			fl->fl_wait  = 0;
 			caller = fl;
 			added = 1;
 		}
diff --git a/fs/minix/Makefile b/fs/minix/Makefile
index 7f7fa3e..20e7f3d 100644
--- a/fs/minix/Makefile
+++ b/fs/minix/Makefile
@@ -20,9 +20,6 @@
 minix.o: $(OBJS)
 	$(LD) -r -o minix.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 64cfb6f..8f4d1c8 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -174,7 +174,7 @@
 	}
 	clear_inode(inode);
 	if (!clear_bit(ino & 8191, bh->b_data))
-		printk("free_inode: bit %d already cleared.\n",ino);
+		printk("free_inode: bit %lu already cleared.\n",ino);
 	bh->b_dirt = 1;
 }
 
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 903318a..8329a48 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -180,10 +180,8 @@
 	if (!read)
 		return -EIO;
 	filp->f_reada = 1;
-	if (!IS_RDONLY(inode)) {
+	if (!IS_RDONLY(inode))
 		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
 	return read;
 }
 
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 206e5ec..97e2920 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -423,7 +423,7 @@
 		wait_on_buffer(bh);
 		if (bh->b_req && !bh->b_uptodate)
 		{
-			printk ("IO error syncing minix inode [%04x:%08x]\n",
+			printk ("IO error syncing minix inode [%04x:%08lx]\n",
 				inode->i_dev, inode->i_ino);
 			err = -1;
 		}
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index f7fd367..f4814ee 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -185,7 +185,6 @@
 			de->inode = 0;
 			dir->i_size = block*bh->b_size + offset;
 			dir->i_dirt = 1;
-			dir->i_ctime = CURRENT_TIME;
 		}
 		if (de->inode) {
 			if (namecompare(namelen, info->s_namelen, name, de->name)) {
@@ -286,7 +285,6 @@
 		init_fifo(inode);
 	if (S_ISBLK(mode) || S_ISCHR(mode))
 		inode->i_rdev = rdev;
-	inode->i_mtime = inode->i_atime = CURRENT_TIME;
 	inode->i_dirt = 1;
 	error = minix_add_entry(dir, name, len, &bh, &de);
 	if (error) {
@@ -334,7 +332,6 @@
 	}
 	inode->i_op = &minix_dir_inode_operations;
 	inode->i_size = 2 * info->s_dirsize;
-	inode->i_mtime = inode->i_atime = CURRENT_TIME;
 	dir_block = minix_bread(inode,0,1);
 	if (!dir_block) {
 		iput(dir);
@@ -461,6 +458,10 @@
 		retval = -ENOTEMPTY;
 		goto end_rmdir;
 	}
+	if (de->inode != inode->i_ino) {
+		retval = -ENOENT;
+		goto end_rmdir;
+	}
 	if (inode->i_count > 1) {
 		retval = -EBUSY;
 		goto end_rmdir;
@@ -472,7 +473,7 @@
 	inode->i_nlink=0;
 	inode->i_dirt=1;
 	dir->i_nlink--;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt=1;
 	retval = 0;
 end_rmdir:
@@ -511,8 +512,12 @@
 	    current->euid != inode->i_uid &&
 	    current->euid != dir->i_uid)
 		goto end_unlink;
+	if (de->inode != inode->i_ino) {
+		retval = -ENOENT;
+		goto end_unlink;
+	}
 	if (!inode->i_nlink) {
-		printk("Deleting nonexistent file (%04x:%d), %d\n",
+		printk("Deleting nonexistent file (%04x:%lu), %d\n",
 			inode->i_dev,inode->i_ino,inode->i_nlink);
 		inode->i_nlink=1;
 	}
@@ -521,7 +526,7 @@
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 	inode->i_nlink--;
-	inode->i_ctime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime;
 	inode->i_dirt = 1;
 	retval = 0;
 end_unlink:
diff --git a/fs/msdos/Makefile b/fs/msdos/Makefile
index b84c536..820ec43 100644
--- a/fs/msdos/Makefile
+++ b/fs/msdos/Makefile
@@ -19,9 +19,6 @@
 msdos.o: $(OBJS)
 	$(LD) -r -o msdos.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/msdos/file.c b/fs/msdos/file.c
index 0bf8f39..fc5463d 100644
--- a/fs/msdos/file.c
+++ b/fs/msdos/file.c
@@ -115,6 +115,10 @@
 					else {
 						filp->f_pos = inode->i_size;
 						brelse(bh);
+						if (start != buf
+						    && !IS_RDONLY(inode))
+							inode->i_atime
+							    = CURRENT_TIME;
 						return buf-start;
 					}
 				}
@@ -122,6 +126,8 @@
 		brelse(bh);
 	}
 	if (start == buf) return -EIO;
+	if (!IS_RDONLY(inode))
+		inode->i_atime = CURRENT_TIME;
 	return buf-start;
 }
 
@@ -197,10 +203,12 @@
 		bh->b_dirt = 1;
 		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 start == buf ? error : buf-start;
+	return buf-start;
 }
 
 
diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c
index 1739403..cf33362 100644
--- a/fs/msdos/inode.c
+++ b/fs/msdos/inode.c
@@ -224,7 +224,7 @@
 		    conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ?
 		    ",bmap" : "");
 		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
-		    "se=%d,ts=%d,ls=%d]\n",b->media,MSDOS_SB(s)->cluster_size,
+		    "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(s)->cluster_size,
 		    MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start,MSDOS_SB(s)->
 		    fat_length,MSDOS_SB(s)->dir_start,MSDOS_SB(s)->dir_entries,
 		    MSDOS_SB(s)->data_start,CF_LE_W(*(unsigned short *) &b->
@@ -333,7 +333,7 @@
 	}
 	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
 	    BLOCK_SIZE))) {
-		printk("dev = 0x%04X, ino = %d\n",inode->i_dev,inode->i_ino);
+		printk("dev = 0x%04X, ino = %ld\n",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))
@@ -356,9 +356,11 @@
 			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 %d: bad FAT\n",
+				if (!(nr = fat_access(inode->i_sb,nr,-1))) {
+					printk("Directory %ld: bad FAT\n",
 					    inode->i_ino);
+					break;
+				}
 			}
 	}
 	else {
@@ -394,7 +396,7 @@
 	if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
 	if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
 	    BLOCK_SIZE))) {
-		printk("dev = 0x%04X, ino = %d\n",inode->i_dev,inode->i_ino);
+		printk("dev = 0x%04X, ino = %ld\n",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))
diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c
index edd704f..49b7215 100644
--- a/fs/msdos/misc.c
+++ b/fs/msdos/misc.c
@@ -33,7 +33,7 @@
 
 	not_ro = !(s->s_flags & MS_RDONLY);
 	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev 0x%04X, mounted on 0x%04X:%d)\n  %s\n",
+	printk("Filesystem panic (dev 0x%04X, mounted on 0x%04X:%ld)\n  %s\n",
 	    s->s_dev,s->s_covered->i_dev,s->s_covered->i_ino,msg);
 	if (not_ro)
 		printk("  File system has been set read-only\n");
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 009aed2..cf64067 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -171,10 +171,15 @@
 		if ((res = msdos_add_cluster(dir)) < 0) return res;
 		if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res;
 	}
+	/*
+	 * XXX all times should be set by caller upon successful completion.
+	 */
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_dirt = 1;
 	memcpy(de->name,name,MSDOS_NAME);
 	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 	de->start = 0;
-	date_unix2dos(CURRENT_TIME,&de->time,&de->date);
+	date_unix2dos(dir->i_mtime,&de->time,&de->date);
 	de->size = 0;
 	bh->b_dirt = 1;
 	if ((*result = iget(dir->i_sb,ino)) != NULL)
@@ -341,7 +346,7 @@
 	if (res)
 		goto rmdir_done;
 	inode->i_nlink = 0;
-	dir->i_mtime = CURRENT_TIME;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_nlink--;
 	inode->i_dirt = dir->i_dirt = 1;
 	de->name[0] = DELETED_FLAG;
@@ -375,8 +380,9 @@
 		goto unlink_done;
 	}
 	inode->i_nlink = 0;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	MSDOS_I(inode)->i_busy = 1;
-	inode->i_dirt = 1;
+	inode->i_dirt = dir->i_dirt = 1;
 	de->name[0] = DELETED_FLAG;
 	bh->b_dirt = 1;
 unlink_done:
diff --git a/fs/namei.c b/fs/namei.c
index 70ae8a7..9273627 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -659,10 +659,12 @@
 	if (error)
 		return error;
 	error = getname(newname,&to);
-	if (!error) {
-		error = do_link(oldinode,to);
-		putname(to);
+	if (error) {
+		iput(oldinode);
+		return error;
 	}
+	error = do_link(oldinode,to);
+	putname(to);
 	return error;
 }
 
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index c51d7f7..8610c95 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -20,9 +20,6 @@
 nfs.o: $(OBJS)
 	$(LD) -r -o nfs.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 4bb519d..2f73cf9 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -113,7 +113,7 @@
 
 static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
 {
-	fattr->type = ntohl(*p++);
+	fattr->type = (enum nfs_stat) ntohl(*p++);
 	fattr->mode = ntohl(*p++);
 	fattr->nlink = ntohl(*p++);
 	fattr->uid = ntohl(*p++);
@@ -676,7 +676,7 @@
 #endif
 
 static struct {
-	enum nfs_stat stat;
+	int stat;
 	int errno;
 } nfs_errtbl[] = {
 	{ NFS_OK,		0		},
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index 323aa88..f355b0c 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -138,6 +138,12 @@
 #endif
 				goto re_select;
 			}
+			if (result == -ECONNREFUSED) {
+#if 0
+				printk("nfs_rpc_call: server playing coy\n");
+#endif
+				goto re_select;
+			}
 			if (result != -ERESTARTSYS) {
 				printk("nfs_rpc_call: recv error = %d\n",
 					-result);
diff --git a/fs/open.c b/fs/open.c
index 6e2c4f8..bef97e9 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -84,7 +84,7 @@
 	inode->i_size = length;
 	if (inode->i_op && inode->i_op->truncate)
 		inode->i_op->truncate(inode);
-	inode->i_atime = inode->i_mtime = CURRENT_TIME;
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	inode->i_dirt = 1;
 	error = notify_change(NOTIFY_SIZE, inode);
 	iput(inode);
@@ -105,7 +105,7 @@
 	inode->i_size = length;
 	if (inode->i_op && inode->i_op->truncate)
 		inode->i_op->truncate(inode);
-	inode->i_atime = inode->i_mtime = CURRENT_TIME;
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	inode->i_dirt = 1;
 	return notify_change(NOTIFY_SIZE, inode);
 }
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index e786446..71c6243 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -19,9 +19,6 @@
 proc.o: $(OBJS)
 	$(LD) -r -o proc.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 017bcf7..28690e6 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -92,7 +92,7 @@
 
 	uptime = jiffies;
 	idle = task[0]->utime + task[0]->stime;
-	return sprintf(buffer,"%d.%02d %d.%02d\n",
+	return sprintf(buffer,"%lu.%02lu %lu.%02lu\n",
 		uptime / HZ,
 		uptime % HZ,
 		idle / HZ,
@@ -106,8 +106,8 @@
 	si_meminfo(&i);
 	si_swapinfo(&i);
 	return sprintf(buffer, "        total:   used:    free:   shared:  buffers:\n"
-		"Mem:  %8d %8d %8d %8d %8d\n"
-		"Swap: %8d %8d %8d\n",
+		"Mem:  %8lu %8lu %8lu %8lu %8lu\n"
+		"Swap: %8lu %8lu %8lu\n",
 		i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram,
 		i.totalswap, i.totalswap-i.freeswap, i.freeswap);
 }
@@ -260,9 +260,9 @@
 		tty_pgrp = tty_table[tty_pgrp]->pgrp;
 	else
 		tty_pgrp = -1;
-	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %u %u \
-%u %u %u %d %d %d %d %d %d %u %u %d %u %u %u %u %u %u %u %u %d \
-%d %d %d %u\n",
+	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
+%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %u %u %lu %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu\n",
 		pid,
 		(*p)->comm,
 		state,
@@ -401,7 +401,7 @@
 			ino = 0;
 		}
 
-		sz += sprintf(buf+sz, "%08x-%08x %s %08x %02x:%02x %d\n",
+		sz += sprintf(buf+sz, "%08lx-%08lx %s %08lx %02x:%02x %lu\n",
 			      map->vm_start, map->vm_end, str, map->vm_offset,
 			      MAJOR(dev),MINOR(dev), ino);
 		if (sz > end) {
diff --git a/fs/super.c b/fs/super.c
index 772bdd1..089f6f3 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -10,6 +10,7 @@
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/string.h>
@@ -25,7 +26,8 @@
  */
 
 extern struct file_system_type file_systems[];
-extern struct file_operations * blkdev_fops[];
+extern struct file_operations * get_blkfops(unsigned int);
+extern struct file_operations * get_chrfops(unsigned int);
 
 extern void wait_for_keypress(void);
 extern void fcntl_init_locks(void);
@@ -217,11 +219,11 @@
 	if (!(sb=get_super(dev)) || !(sb->s_covered))
 		return -ENOENT;
 	if (!sb->s_covered->i_mount)
-		printk("VFS: umount(%d/%d): mounted inode has i_mount=0\n",
+		printk("VFS: umount(%d/%d): mounted inode has i_mount=NULL\n",
 							MAJOR(dev), MINOR(dev));
 	if (!fs_may_umount(dev, sb->s_mounted))
 		return -EBUSY;
-	sb->s_covered->i_mount=0;
+	sb->s_covered->i_mount = NULL;
 	iput(sb->s_covered);
 	sb->s_covered = NULL;
 	iput(sb->s_mounted);
@@ -281,7 +283,7 @@
 		return -ENXIO;
 	}
 	if (!(retval = do_umount(dev)) && dev != ROOT_DEV) {
-		fops = blkdev_fops[MAJOR(dev)];
+		fops = get_blkfops(MAJOR(dev));
 		if (fops && fops->release)
 			fops->release(inode,NULL);
 		if (MAJOR(dev) == UNNAMED_MAJOR)
@@ -331,7 +333,7 @@
 		return -EBUSY;
 	}
 	sb->s_covered = dir_i;
-	dir_i->i_mount = 1;
+	dir_i->i_mount = sb->s_mounted;
 	return 0;		/* we don't iput(dir_i) - see umount */
 }
 
@@ -443,7 +445,7 @@
 			return -EMFILE;
 		inode = NULL;
 	}
-	fops = blkdev_fops[MAJOR(dev)];
+	fops = get_blkfops(MAJOR(dev));
 	if (fops && fops->open) {
 		retval = fops->open(inode,NULL);
 		if (retval) {
@@ -454,17 +456,24 @@
 	if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) {
 		flags = new_flags & ~MS_MGC_MSK;
 		if (data) {
-			if ((unsigned long) data >= TASK_SIZE) {
-				iput(inode);
-				return -EFAULT;
+			struct vm_area_struct * vma;
+
+			for (vma = current->mmap ; ; ) {
+				if (!vma || (unsigned long) data < vma->vm_start) {
+					iput(inode);
+					return -EFAULT;
+				}
+				if ((unsigned long) data < vma->vm_end)
+					break;
+				vma = vma->vm_next;
 			}
+			i = vma->vm_end - (unsigned long) data;
+			if (PAGE_SIZE <= (unsigned long) i)
+				i = PAGE_SIZE-1;
 			if (!(page = __get_free_page(GFP_KERNEL))) {
 				iput(inode);
 				return -ENOMEM;
 			}
-			i = TASK_SIZE - (unsigned long) data;
-			if ((unsigned long) i >= PAGE_SIZE)
-				i = PAGE_SIZE-1;
 			memcpy_fromfs((void *) page,data,i);
 		}
 	}
@@ -484,8 +493,8 @@
 
 	memset(super_blocks, 0, sizeof(super_blocks));
 	fcntl_init_locks();
-	if (MAJOR(ROOT_DEV) == 2) {
-		printk("VFS: Insert root floppy and press ENTER\n");
+	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
+		printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
 		wait_for_keypress();
 	}
 	for (fs_type = file_systems; fs_type->read_super; fs_type++) {
diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile
index 4cc1cb0..0975632 100644
--- a/fs/xiafs/Makefile
+++ b/fs/xiafs/Makefile
@@ -20,12 +20,6 @@
 xiafs.o: $(OBJS)
 	$(LD) -r -o xiafs.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
-veryclean:
-	rm -f core *.o *.a *.s *~ .depend tags
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c
index fceb732..3dd74fa 100644
--- a/fs/xiafs/bitmap.c
+++ b/fs/xiafs/bitmap.c
@@ -242,7 +242,9 @@
 	return;
     offset = bit & (XIAFS_BITS_PER_Z(sb) -1);
     if (clear_bit(offset, bh->b_data))
-        printk("XIA-FS: bit %d (0x%x) already cleared (%s %d)\n", bit, bit, WHERE_ERR);
+        printk("XIA-FS: dev %04x"
+	       " block bit %u (0x%x) already cleared (%s %d)\n",
+	       sb->s_dev, bit, bit, WHERE_ERR);
     bh->b_dirt = 1;
     xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
 }
@@ -300,8 +302,9 @@
 	return;
     clear_inode(inode);
     if (clear_bit(ino & (XIAFS_BITS_PER_Z(sb)-1), bh->b_data))
-        printk("XIA-FS: bit %d (0x%x) already cleared (%s %d)\n",
-		ino, ino, WHERE_ERR);
+        printk("XIA-FS: dev %04x"
+	       "inode bit %ld (0x%lx) already cleared (%s %d)\n",
+	       inode->i_dev, ino, ino, WHERE_ERR);
     bh->b_dirt = 1;
     xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached);
 }
diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c
index 44e9e24..3c5bd5a 100644
--- a/fs/xiafs/file.c
+++ b/fs/xiafs/file.c
@@ -243,13 +243,9 @@
 	bh->b_dirt = 1;
 	brelse(bh);
     }
-    inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+    inode->i_mtime = inode->i_ctime = CURRENT_TIME;
     filp->f_pos = pos;
     inode->i_dirt = 1;
 
     return written;
 }
-
-
-
-
diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c
index 2c29778..84e2109 100644
--- a/fs/xiafs/inode.c
+++ b/fs/xiafs/inode.c
@@ -490,8 +490,8 @@
     	wait_on_buffer(bh);
     	if (bh->b_req && !bh->b_uptodate)
     	{
-    	    printk ("IO error syncing xiafs inode [%04x:%08x]\n",
-    	    	inode->i_dev, inode->i_ino);
+    	    printk ("IO error syncing xiafs inode [%04X:%lu]\n",
+		    inode->i_dev, inode->i_ino);
     	    err = -1;
     	}
     }
diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c
index 2dbbf34..52c9a68 100644
--- a/fs/xiafs/namei.c
+++ b/fs/xiafs/namei.c
@@ -212,7 +212,11 @@
 		}
 	    }
 	    if (!de->d_ino && RNDUP4(namelen)+8 <= de->d_rec_len) {
-	        dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		/*
+		 * XXX all times should be set by caller upon successful
+		 * completion.
+		 */
+	        dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 		dir->i_dirt = 1;
 		memcpy(de->d_name, name, namelen);
 		de->d_name[namelen]=0;
@@ -518,7 +522,7 @@
     inode->i_nlink=0;
     inode->i_dirt=1;
     dir->i_nlink--;
-    dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+    inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
     dir->i_dirt=1;
     retval = 0;
 end_rmdir:
@@ -563,7 +567,7 @@
     }
     xiafs_rm_entry(de, de_pre);
     bh->b_dirt = 1;
-    dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+    inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
     dir->i_dirt = 1;
     inode->i_nlink--;
     inode->i_dirt = 1;
@@ -661,7 +665,7 @@
     brelse(bh);
     iput(dir);
     oldinode->i_nlink++;
-    oldinode->i_atime = oldinode->i_ctime = CURRENT_TIME;
+    oldinode->i_ctime = CURRENT_TIME;
     oldinode->i_dirt = 1;
     iput(oldinode);
     return 0;
@@ -841,8 +845,3 @@
     wake_up(&wait);
     return result;
 }
-
-
-
-
-
diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c
index 7b95b37..6d7cb7f 100644
--- a/fs/xiafs/truncate.c
+++ b/fs/xiafs/truncate.c
@@ -193,9 +193,6 @@
 	current->counter = 0;
 	schedule();
     }
-    inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+    inode->i_ctime = inode->i_mtime = CURRENT_TIME;
     inode->i_dirt = 1;
 }
-
-
-
diff --git a/ibcs/Makefile b/ibcs/Makefile
index 6b646a9..a04f9bd 100644
--- a/ibcs/Makefile
+++ b/ibcs/Makefile
@@ -24,13 +24,9 @@
 	$(LD) -r -o ibcs.o $(OBJS)
 	sync
 
-clean:
-	rm -f core *.o *.a *.s
-	for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
-
 dep:
 	$(CPP) -M *.c > .depend
-	for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
 
 dummy:
 
diff --git a/ibcs/emulate.c b/ibcs/emulate.c
index cb4f9ec..42a8984 100644
--- a/ibcs/emulate.c
+++ b/ibcs/emulate.c
@@ -22,5 +22,5 @@
 
 asmlinkage void iABI_emulate(struct pt_regs * regs)
 {
-	printk("lcall 7,xxx: eax = %08x\n",regs->eax);
+	printk("lcall 7,xxx: eax = %08lx\n",regs->eax);
 }
diff --git a/include/asm/io.h b/include/asm/io.h
index b405c2d..c0cbcd1 100644
--- a/include/asm/io.h
+++ b/include/asm/io.h
@@ -7,7 +7,7 @@
  * to guarantee better timings even on fast machines.
  *
  * On the other hand, I'd like to be sure of a non-existent port:
- * I feel a bit unsafe abou using 0x80.
+ * I feel a bit unsafe about using 0x80.
  *
  *		Linus
  */
@@ -94,7 +94,7 @@
 
 /*
  * Note that due to the way __builtin_constant_p() works, you
- *  - can't use it inside a inlien function (it will never be true)
+ *  - can't use it inside a inline function (it will never be true)
  *  - you don't have to worry about side effects within the __builtin..
  */
 #define outb(val,port) \
diff --git a/include/linux/config.h b/include/linux/config.h
index 81fe479..f4ed908 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -30,7 +30,7 @@
 #define DEF_INITSEG	0x9000
 #define DEF_SYSSEG	0x1000
 #define DEF_SETUPSEG	0x9020
-#define DEF_SYSSIZE	0x8000
+#define DEF_SYSSIZE	0x7F00
 
 /* internal svga startup constants */
 #define NORMAL_VGA	0xffff		/* 80x25 mode */
diff --git a/include/linux/errno.h b/include/linux/errno.h
index 792213f..b2a8ec0 100644
--- a/include/linux/errno.h
+++ b/include/linux/errno.h
@@ -122,6 +122,7 @@
 #define	ENAVAIL		119	/* No XENIX semaphores available */
 #define	EISNAM		120	/* Is a named type file */
 #define	EREMOTEIO	121	/* Remote I/O error */
+#define	EDQUOT		122	/* Quota exceeded */
 
 /* Should never be seen by user programs */
 #define ERESTARTSYS	512
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 00c9806..168f978 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -1,3 +1,15 @@
+/*
+ *  linux/include/linux/ext2_fs.h
+ *
+ *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
 #ifndef _LINUX_EXT2_FS_H
 #define _LINUX_EXT2_FS_H
 
@@ -11,25 +23,48 @@
 #undef EXT2FS_DEBUG
 
 /*
- * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b
- */
-#undef EXT2FS_PRE_02B_COMPAT
-
-/*
- * Define DONT_USE_DCACHE to inhibit the directory cache
- */
-#undef DONT_USE_DCACHE
-
-/*
  * Define EXT2FS_DEBUG_CACHE to produce cache debug messages
  */
 #undef EXT2FS_DEBUG_CACHE
 
 /*
+ * Define EXT2FS_CHECK_CACHE to add some checks to the name cache code
+ */
+#undef EXT2FS_CHECK_CACHE
+
+/*
+ * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b
+ */
+#undef EXT2FS_PRE_02B_COMPAT
+
+/*
+ * Define EXT2FS_PRE_04_COMPAT to convert ext2 fs prior to 0.4
+ */
+#define EXT2_PRE_04_COMPAT
+
+/*
+ * Define DONT_USE_DCACHE to inhibit the directory cache
+ */
+#define DONT_USE_DCACHE
+
+/*
  * The second extended file system version
  */
-#define EXT2FS_DATE		"93/08/05"
-#define EXT2FS_VERSION		"0.3c"
+#define EXT2FS_DATE		"93/11/19"
+#define EXT2FS_VERSION		"0.4a"
+
+/*
+ * Debug code
+ */
+#ifdef EXT2FS_DEBUG
+#	define ext2_debug(f, a...)	{ \
+					printk ("EXT2-fs DEBUG (%s, %d): %s:", \
+						__FILE__, __LINE__, __FUNCTION__); \
+				  	printk (f, ## a); \
+					}
+#else
+#	define ext2_debug(f, a...)	/**/
+#endif
 
 /*
  * Special inodes numbers
@@ -39,12 +74,13 @@
 #define EXT2_ACL_IDX_INO	 3	/* ACL inode */
 #define EXT2_ACL_DATA_INO	 4	/* ACL inode */
 #define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
 #define EXT2_FIRST_INO		11	/* First non reserved inode */
 
 /*
  * The second extended file system magic number
  */
-#define EXT2_OLD_SUPER_MAGIC	0xEF51
+#define EXT2_PRE_02B_MAGIC	0xEF51
 #define EXT2_SUPER_MAGIC	0xEF53
 
 /*
@@ -155,6 +191,22 @@
 #define	EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)
 
 /*
+ * Inode flags
+ */
+#define	EXT2_SECRM_FL			0x0001	/* Secure deletion */
+#define	EXT2_UNRM_FL			0x0002	/* Undelete */
+#define	EXT2_COMPR_FL			0x0004	/* Compress file */
+#define EXT2_SYNC_FL			0x0008	/* Synchronous updates */
+
+/*
+ * ioctl commands
+ */
+#define	EXT2_IOC_GETFLAGS		_IOR('f', 1, long)
+#define	EXT2_IOC_SETFLAGS		_IOW('f', 2, long)
+#define	EXT2_IOC_GETVERSION		_IOR('v', 1, long)
+#define	EXT2_IOC_SETVERSION		_IOW('v', 2, long)
+
+/*
  * Structure of an inode on the disk
  */
 struct ext2_inode {
@@ -182,6 +234,22 @@
 };
 
 /*
+ * File system states
+ */
+#define	EXT2_VALID_FS			0x0001	/* Unmounted cleany */
+#define	EXT2_ERROR_FS			0x0002	/* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK		0x0001	/* Do some more checks */
+
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT		20	/* Allow 20 mounts */
+
+/*
  * Structure of the super block
  */
 struct ext2_super_block {
@@ -198,12 +266,11 @@
 	unsigned long  s_inodes_per_group;/* # Inodes per group */
 	unsigned long  s_mtime;		/* Mount time */
 	unsigned long  s_wtime;		/* Write time */
-	unsigned long  s_pad;		/* Padding to get the magic signature*/
-					/* at the same offset as in the */
-					/* previous ext fs */
+	unsigned short s_mnt_count;	/* Mount count */
+	unsigned short s_max_mnt_count;	/* Maximal mount count */
 	unsigned short s_magic;		/* Magic signature */
-	unsigned short s_valid;		/* Flag */
-	unsigned long  s_reserved[243];	/* Padding to the end of the block */
+	unsigned short s_state;		/* File system state */
+	unsigned long  s_reserved[241];	/* Padding to the end of the block */
 };
 
 /*
@@ -240,23 +307,26 @@
 extern int ext2_new_block (struct super_block *, unsigned long);
 extern void ext2_free_block (struct super_block *, unsigned long);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
 
 /* bitmap.c */
 extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
 
+#ifndef DONT_USE_DCACHE
 /* dcache.c */
 extern void ext2_dcache_invalidate (unsigned short);
 extern unsigned long ext2_dcache_lookup (unsigned short, unsigned long,
 					 const char *, int);
 extern void ext2_dcache_add (unsigned short, unsigned long, const char *,
-			     int, int);
+			     int, unsigned long);
 extern void ext2_dcache_remove (unsigned short, unsigned long, const char *,
 				int);
+#endif
 
 /* dir.c */
 extern int ext2_check_dir_entry (char *, struct inode *,
 				 struct ext2_dir_entry *, struct buffer_head *,
-				 unsigned int);
+				 unsigned long);
 
 /* file.c */
 extern int ext2_read (struct inode *, struct file *, char *, int);
@@ -269,22 +339,18 @@
 extern struct inode * ext2_new_inode (const struct inode *, int);
 extern void ext2_free_inode (struct inode *);
 extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
 
 /* inode.c */
 extern int ext2_bmap (struct inode *, int);
 
-extern struct buffer_head * ext2_getblk (struct inode *, int, int, int *);
+extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
 extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
 
-extern void ext2_put_super (struct super_block *);
-extern void ext2_write_super (struct super_block *);
-extern int ext2_remount (struct super_block *, int *);
-extern struct super_block * ext2_read_super (struct super_block *,void *,int);
 extern void ext2_read_inode (struct inode *);
 extern void ext2_write_inode (struct inode *);
 extern void ext2_put_inode (struct inode *);
-extern void ext2_statfs (struct super_block *, struct statfs *);
-extern int ext2_sync_inode(struct inode *);
+extern int ext2_sync_inode (struct inode *);
 
 /* ioctl.c */
 extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
@@ -305,6 +371,20 @@
 extern int ext2_rename (struct inode *, const char *, int,
 			struct inode *, const char *, int);
 
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern volatile void ext2_panic (struct super_block *, const char *,
+				 const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern void ext2_statfs (struct super_block *, struct statfs *);
+
 /* truncate.c */
 extern void ext2_truncate (struct inode *);
 
@@ -321,6 +401,6 @@
 /* symlink.c */
 extern struct inode_operations ext2_symlink_inode_operations;
 
-#endif
+#endif	/* __KERNEL__ */
 
-#endif
+#endif	/* _LINUX_EXT2_FS_H */
diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h
index ffb1ba6..33fda9f 100644
--- a/include/linux/ext2_fs_i.h
+++ b/include/linux/ext2_fs_i.h
@@ -1,5 +1,17 @@
-#ifndef _EXT2_FS_I
-#define _EXT2_FS_I
+/*
+ *  linux/include/linux/ext2_fs_i.h
+ *
+ *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs_i.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_I
+#define _LINUX_EXT2_FS_I
 
 /*
  * second extended file system inode data in memory
@@ -20,4 +32,4 @@
 	unsigned long  i_next_alloc_goal;
 };
 
-#endif
+#endif	/* _LINUX_EXT2_FS_I */
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index f004e14..5d58f0c 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -1,5 +1,17 @@
-#ifndef _EXT2_FS_SB
-#define _EXT2_FS_SB
+/*
+ *  linux/include/linux/ext2_fs_sb.h
+ *
+ *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs_sb.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_SB
+#define _LINUX_EXT2_FS_SB
 
 #define EXT2_MAX_GROUP_DESC	8
 #define EXT2_MAX_GROUP_LOADED	8
@@ -26,8 +38,9 @@
 	unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
 	struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
 	int s_rename_lock;
-	int s_was_mounted_valid;
 	struct wait_queue * s_rename_wait;
+	unsigned long  s_mount_opt;
+	unsigned short s_mount_state;
 };
 
-#endif
+#endif	/* _LINUX_EXT2_FS_SB */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 69d4784..201baad 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -32,37 +32,6 @@
 #define NR_FILE_LOCKS 64
 #define BLOCK_SIZE 1024
 #define BLOCK_SIZE_BITS 10
-#define MAX_CHRDEV 32
-#define MAX_BLKDEV 32
-
-/* devices are as follows: (same as minix, so we can use the minix
- * file system. These are major numbers.)
- *
- *  0 - unnamed (minor 0 = true nodev)
- *  1 - /dev/mem
- *  2 - /dev/fd
- *  3 - /dev/hd
- *  4 - /dev/ttyx
- *  5 - /dev/tty
- *  6 - /dev/lp
- *  7 -
- *  8 - /dev/sd
- *  9 - /dev/st
- * 10 - mice
- * 11 - scsi cdrom
- * 12 -
- * 13 -
- * 14 - sound card (?)
- * 15 -
- * 16 - 
- * 17 - 
- * 18 - 
- * 19 - 
- * 20 - 
- * 21 - /dev/sg
- */
-
-#define UNNAMED_MAJOR 0
 
 #define MAY_EXEC 1
 #define MAY_WRITE 2
@@ -77,8 +46,8 @@
 extern unsigned long inode_init(unsigned long start, unsigned long end);
 extern unsigned long file_table_init(unsigned long start, unsigned long end);
 
-#define MAJOR(a) (((unsigned)(a))>>8)
-#define MINOR(a) ((a)&0xff)
+#define MAJOR(a) (int)((unsigned short)(a) >> 8)
+#define MINOR(a) (int)((unsigned short)(a) & 0xFF)
 
 #ifndef NULL
 #define NULL ((void *) 0)
@@ -179,6 +148,7 @@
 #include <linux/minix_fs_i.h>
 #include <linux/ext_fs_i.h>
 #include <linux/ext2_fs_i.h>
+#include <linux/hpfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
 #include <linux/iso_fs_i.h>
 #include <linux/nfs_fs_i.h>
@@ -206,12 +176,12 @@
 	struct inode * i_next, * i_prev;
 	struct inode * i_hash_next, * i_hash_prev;
 	struct inode * i_bound_to, * i_bound_by;
+	struct inode * i_mount;
 	unsigned short i_count;
 	unsigned short i_flags;
 	unsigned char i_lock;
 	unsigned char i_dirt;
 	unsigned char i_pipe;
-	unsigned char i_mount;
 	unsigned char i_seek;
 	unsigned char i_update;
 	union {
@@ -219,6 +189,7 @@
 		struct minix_inode_info minix_i;
 		struct ext_inode_info ext_i;
 		struct ext2_inode_info ext2_i;
+		struct hpfs_inode_info hpfs_i;
 		struct msdos_inode_info msdos_i;
 		struct iso_inode_info isofs_i;
 		struct nfs_inode_info nfs_i;
@@ -252,6 +223,7 @@
 #include <linux/minix_fs_sb.h>
 #include <linux/ext_fs_sb.h>
 #include <linux/ext2_fs_sb.h>
+#include <linux/hpfs_fs_sb.h>
 #include <linux/msdos_fs_sb.h>
 #include <linux/iso_fs_sb.h>
 #include <linux/nfs_fs_sb.h>
@@ -275,6 +247,7 @@
 		struct minix_sb_info minix_sb;
 		struct ext_sb_info ext_sb;
 		struct ext2_sb_info ext2_sb;
+		struct hpfs_sb_info hpfs_sb;
 		struct msdos_sb_info msdos_sb;
 		struct isofs_sb_info isofs_sb;
 		struct nfs_sb_info nfs_sb;
diff --git a/include/linux/hpfs_fs.h b/include/linux/hpfs_fs.h
new file mode 100644
index 0000000..0094c35
--- /dev/null
+++ b/include/linux/hpfs_fs.h
@@ -0,0 +1,12 @@
+#ifndef _LINUX_HPFS_FS_H
+#define _LINUX_HPFS_FS_H
+
+/* HPFS magic number (word 0 of block 16) */
+
+#define HPFS_SUPER_MAGIC 0xf995e849
+
+/* The entry point for a VFS */
+
+extern struct super_block *hpfs_read_super (struct super_block *, void *, int);
+
+#endif
diff --git a/include/linux/hpfs_fs_i.h b/include/linux/hpfs_fs_i.h
new file mode 100644
index 0000000..d9aa8f3
--- /dev/null
+++ b/include/linux/hpfs_fs_i.h
@@ -0,0 +1,24 @@
+#ifndef _HPFS_FS_I
+#define _HPFS_FS_I
+
+struct hpfs_inode_info {
+	ino_t i_parent_dir;	/* (directories) gives fnode of parent dir */
+	unsigned i_dno;		/* (directories) root dnode */
+	unsigned i_dpos;	/* (directories) temp for readdir */
+	unsigned i_dsubdno;	/* (directories) temp for readdir */
+	unsigned i_file_sec;	/* (files) minimalist cache of alloc info */
+	unsigned i_disk_sec;	/* (files) minimalist cache of alloc info */
+	unsigned i_n_secs;	/* (files) minimalist cache of alloc info */
+	unsigned i_conv : 2;	/* (files) crlf->newline hackery */
+};
+
+#define i_hpfs_dno u.hpfs_i.i_dno
+#define i_hpfs_parent_dir u.hpfs_i.i_parent_dir
+#define i_hpfs_n_secs u.hpfs_i.i_n_secs
+#define i_hpfs_file_sec u.hpfs_i.i_file_sec
+#define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
+#define i_hpfs_dpos u.hpfs_i.i_dpos
+#define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
+#define i_hpfs_conv u.hpfs_i.i_conv
+
+#endif
diff --git a/include/linux/hpfs_fs_sb.h b/include/linux/hpfs_fs_sb.h
new file mode 100644
index 0000000..a383e16
--- /dev/null
+++ b/include/linux/hpfs_fs_sb.h
@@ -0,0 +1,32 @@
+#ifndef _HPFS_FS_SB
+#define _HPFS_FS_SB
+
+struct hpfs_sb_info {
+	ino_t sb_root;			/* inode number of root dir */
+	unsigned sb_fs_size;		/* file system size, sectors */
+	unsigned sb_bitmaps;		/* sector number of bitmap list */
+	unsigned sb_dirband_size;	/* directory band size, dnodes */
+	unsigned sb_dmap;		/* sector number of dnode bit map */
+	unsigned sb_n_free;		/* free blocks for statfs, or -1 */
+	unsigned sb_n_free_dnodes;	/* free dnodes for statfs, or -1 */
+	uid_t sb_uid;			/* uid from mount options */
+	gid_t sb_gid;			/* gid from mount options */
+	umode_t sb_mode;		/* mode from mount options */
+	unsigned sb_lowercase : 1;	/* downcase filenames hackery */
+	unsigned sb_conv : 2;		/* crlf->newline hackery */
+};
+
+#define s_hpfs_root u.hpfs_sb.sb_root
+#define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
+#define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
+#define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
+#define s_hpfs_dmap u.hpfs_sb.sb_dmap
+#define s_hpfs_uid u.hpfs_sb.sb_uid
+#define s_hpfs_gid u.hpfs_sb.sb_gid
+#define s_hpfs_mode u.hpfs_sb.sb_mode
+#define s_hpfs_n_free u.hpfs_sb.sb_n_free
+#define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
+#define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
+#define s_hpfs_conv u.hpfs_sb.sb_conv
+
+#endif
diff --git a/include/linux/in.h b/include/linux/in.h
index 12e3865..5ec5991 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -77,11 +77,11 @@
 #define	IN_CLASSC_NSHIFT	8
 #define	IN_CLASSC_HOST		(0xffffffff & ~IN_CLASSC_NET)
 
-#define	IN_CLASSD(a)		((((long int) (a)) & 0xf0000000) = 0xe0000000)
+#define	IN_CLASSD(a)		((((long int) (a)) & 0xf0000000) == 0xe0000000)
 #define	IN_MULTICAST(a)		IN_CLASSD(a)
 
-#define	IN_EXPERIMENTAL(a)	((((long int) (a)) & 0xe0000000) = 0xe0000000)
-#define	IN_BADCLASS(a)		((((long int) (a)) & 0xf0000000) = 0xf0000000)
+#define	IN_EXPERIMENTAL(a)	((((long int) (a)) & 0xe0000000) == 0xe0000000)
+#define	IN_BADCLASS(a)		((((long int) (a)) & 0xf0000000) == 0xf0000000)
 
 /* Address to accept any incoming messages. */
 #define	INADDR_ANY		((unsigned long int) 0x00000000)
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 85de5ca..03af7d5 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -27,4 +27,14 @@
 	__asm__ __volatile__("btsl %1,%0":"=m" (bh_active):"ir" (nr));
 }
 
+extern inline void disable_bh(int nr)
+{
+	__asm__ __volatile__("btcl %1,%0":"=m" (bh_mask):"ir" (nr));
+}
+
+extern inline void enable_bh(int nr)
+{
+	__asm__ __volatile__("btsl %1,%0":"=m" (bh_mask):"ir" (nr));
+}
+
 #endif
diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h
index 66c0993..0ff0e5d 100644
--- a/include/linux/iso_fs.h
+++ b/include/linux/iso_fs.h
@@ -191,7 +191,6 @@
 extern struct inode_operations isofs_fifo_inode_operations;
 
 struct lookup_cache{
-  char lock;
   unsigned long dir; /* If this matches... */
   dev_t dev;  /* And this matches */
   unsigned short dlen; /* and this matches... */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 9bbb2f6..f193f2e 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -28,13 +28,30 @@
 #define	KERN_INFO	"<6>"	/* informational			*/
 #define	KERN_DEBUG	"<7>"	/* debug-level messages			*/
 
+#if __GNUC__ < 2 || __GNUC_MINOR__ < 5
+# define NORET_TYPE    __volatile__
+# define ATTRIB_NORET  /**/
+# define NORET_AND     /**/
+#else
+# define NORET_TYPE    /**/
+# define ATTRIB_NORET  __attribute__((noreturn))
+# define NORET_AND     noreturn,
+#endif
+
 extern void math_error(void);
-volatile void panic(const char * fmt, ...)
-	__attribute__ ((format (printf, 1, 2)));
-volatile void do_exit(long error_code);
+NORET_TYPE void panic(const char * fmt, ...)
+	__attribute__ ((NORET_AND format (printf, 1, 2)));
+NORET_TYPE void do_exit(long error_code)
+	ATTRIB_NORET;
 unsigned long simple_strtoul(const char *,char **,unsigned int);
 int sprintf(char * buf, const char * fmt, ...);
 
+int session_of_pgrp(int pgrp);
+
+int kill_proc(int pid, int sig, int priv);
+int kill_pg(int pgrp, int sig, int priv);
+int kill_sl(int sess, int sig, int priv);
+
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
 
diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h
index 6fdaaae..8b0fcab 100644
--- a/include/linux/keyboard.h
+++ b/include/linux/keyboard.h
@@ -5,6 +5,19 @@
 #define set_leds() mark_bh(KEYBOARD_BH)
 
 /*
+ * "dead" keys - prefix key values that are valid only for the next
+ * character code (sticky shift, E0/E1 special scancodes, diacriticals)
+ */
+extern unsigned long kbd_dead_keys;
+extern unsigned long kbd_prev_dead_keys;
+
+/*
+ * these are the hardcoded dead key flags
+ */
+#define KGD_E0		0
+#define KGD_E1		1
+
+/*
  * kbd->xxx contains the VC-local things (flag settings etc..)
  * The low 3 local flags are hardcoded to be the led setting..
  */
@@ -39,6 +52,26 @@
 
 extern unsigned long kbd_init(unsigned long);
 
+extern inline int kbd_dead(int flag)
+{
+	return kbd_prev_dead_keys & (1 << flag);
+}
+
+extern inline void set_kbd_dead(int flag)
+{
+	kbd_dead_keys |= 1 << flag;
+}
+
+extern inline void clr_kbd_dead(int flag)
+{
+	kbd_dead_keys &= ~(1 << flag);
+}
+
+extern inline void chg_kbd_dead(int flag)
+{
+	kbd_dead_keys ^= 1 << flag;
+}
+
 extern inline int vc_kbd_flag(struct kbd_struct * kbd, int flag)
 {
 	return ((kbd->flags >> flag) & 1);
@@ -65,7 +98,7 @@
 extern const int max_vals[];
 extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
 
-#define NR_FUNC 35
+#define NR_FUNC 32
 #define FUNC_BUFSIZE 512
 extern char func_buf[FUNC_BUFSIZE];
 extern char *func_table[NR_FUNC];
@@ -112,9 +145,6 @@
 #define K_SELECT	K(KT_FN,23)
 #define K_PGUP		K(KT_FN,24)
 #define K_PGDN		K(KT_FN,25)
-#define K_MACRO         K(KT_FN,26)
-#define K_HELP          K(KT_FN,27)
-#define K_DO            K(KT_FN,28)
 
 #define K_HOLE		K(KT_SPEC,0)
 #define K_ENTER		K(KT_SPEC,1)
@@ -130,7 +160,6 @@
 #define K_SCROLLBACK	K(KT_SPEC,11)
 #define K_BOOT		K(KT_SPEC,12)
 #define K_CAPSON	K(KT_SPEC,13)
-#define K_DEADNEXT      K(KT_SPEC,14)
 
 #define K_P0		K(KT_PAD,0)
 #define K_P1		K(KT_PAD,1)
@@ -149,7 +178,6 @@
 #define K_PENTER	K(KT_PAD,14)	/* key-pad enter		   */
 #define K_PCOMMA	K(KT_PAD,15)	/* key-pad comma: kludge...	   */
 #define K_PDOT		K(KT_PAD,16)	/* key-pad dot (period): kludge... */
-#define K_PPLUSMINUS    K(KT_PAD,17)    /* key-pad plus/minus              */
 
 #define K_DGRAVE	K(KT_DEAD,0)
 #define K_DACUTE	K(KT_DEAD,1)
diff --git a/include/linux/major.h b/include/linux/major.h
new file mode 100644
index 0000000..463d1ae
--- /dev/null
+++ b/include/linux/major.h
@@ -0,0 +1,84 @@
+#ifndef _LINUX_MAJOR_H
+#define _LINUX_MAJOR_H
+
+/*
+ * This file has definitions for major device numbers
+ */
+
+/* limits */
+
+#define MAX_CHRDEV 32
+#define MAX_BLKDEV 32
+
+/*
+ * assignments
+ *
+ * devices are as follows (same as minix, so we can use the minix fs):
+ *
+ *      character              block                  comments
+ *      --------------------   --------------------   --------------------
+ *  0 - unnamed                unnamed                minor 0 = true nodev
+ *  1 - /dev/mem               ramdisk
+ *  2 -                        floppy
+ *  3 -                        hd
+ *  4 - /dev/tty*
+ *  5 - /dev/tty; /dev/cua*
+ *  6 - lp
+ *  7 -                                               UNUSED
+ *  8 -                        scsi disk
+ *  9 - scsi tape
+ * 10 - mice
+ * 11 -                        scsi cdrom
+ * 12 - qic02 tape
+ * 13 -                        xt disk
+ * 14 - sound card
+ * 15 -                        cdu31a cdrom
+ * 16 - sockets
+ * 17 - af_unix
+ * 18 - af_inet
+ * 19 -                                               UNUSED
+ * 20 -                                               UNUSED
+ * 21 - scsi generic
+ * 22 -                                               UNUSED
+ * 23 -                        mitsumi cdrom
+ */
+
+#define UNNAMED_MAJOR	0
+#define MEM_MAJOR	1
+#define FLOPPY_MAJOR	2
+#define HD_MAJOR	3
+#define TTY_MAJOR	4
+#define TTYAUX_MAJOR	5
+#define LP_MAJOR	6
+/* unused: 7 */
+#define SCSI_DISK_MAJOR	8
+#define SCSI_TAPE_MAJOR	9
+#define MOUSE_MAJOR	10
+#define SCSI_CDROM_MAJOR 11
+#define QIC02_TAPE_MAJOR 12
+#define XT_DISK_MAJOR	13
+#define SOUND_MAJOR	14
+#define CDU31A_CDROM_MAJOR 15
+#define SOCKET_MAJOR	16
+#define AF_UNIX_MAJOR	17
+#define AF_INET_MAJOR	18
+/* unused: 19, 20 */
+#define SCSI_GENERIC_MAJOR 21
+/* unused: 22 */
+#define MITSUMI_CDROM_MAJOR 23
+
+/*
+ * Tests for SCSI devices.
+ */
+
+#define SCSI_MAJOR(M) \
+  ((M) == SCSI_DISK_MAJOR	\
+   || (M) == SCSI_TAPE_MAJOR	\
+   || (M) == SCSI_CDROM_MAJOR	\
+   || (M) == SCSI_GENERIC_MAJOR)
+
+static inline int scsi_major(int m) {
+	return SCSI_MAJOR(m);
+}
+
+#endif
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 875e106..89e7fe2 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -114,6 +114,7 @@
 
 extern void * vmalloc(unsigned long size);
 extern void vfree(void * addr);
+extern int vread(char *buf, char *addr, int count);
 
 /* swap.c */
 
diff --git a/include/linux/net.h b/include/linux/net.h
index c861208..2f6d08e 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -24,7 +24,6 @@
 
 #define NSOCKETS	128		/* should be dynamic, later...	*/
 #define NPROTO		16		/* should be enough for now..	*/
-#define SOCKET_MAJOR	16		/* Linux VFS major dev number	*/
 
 
 #define SYS_SOCKET	1		/* sys_socket(2)		*/
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 3a943dd..4ea4e25 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -83,8 +83,6 @@
 extern void sched_init(void);
 extern void show_state(void);
 extern void trap_init(void);
-extern volatile void panic(const char * fmt, ...)
-	__attribute__ ((format (printf, 1, 2)));
 
 asmlinkage void schedule(void);
 
@@ -172,7 +170,7 @@
 	int swappable:1;
 	unsigned long start_code,end_code,end_data,start_brk,brk,start_stack,start_mmap;
 	unsigned long arg_start, arg_end, env_start, env_end;
-	long pid,pgrp,session,leader;
+	int pid,pgrp,session,leader;
 	int	groups[NGROUPS];
 	/* 
 	 * pointers to (original) parent process, youngest child, younger sibling,
@@ -183,7 +181,7 @@
 	struct wait_queue *wait_chldexit;	/* for wait4() */
 	/*
 	 * For ease of programming... Normal sleeps don't need to
-	 * keep track of a wait-queue: every task has an entry of it's own
+	 * keep track of a wait-queue: every task has an entry of its own
 	 */
 	unsigned short uid,euid,suid;
 	unsigned short gid,egid,sgid;
@@ -249,7 +247,7 @@
 /* debugregs */ { 0, },            \
 /* schedlink */	&init_task,&init_task, \
 /* signals */	{{ 0, },}, \
-/* stack */	0,0, \
+/* stack */	0,(unsigned long) &init_kernel_stack, \
 /* ec,brk... */	0,0,0,0,0,0,0,0,0,0,0,0, \
 /* argv.. */	0,0,0,0, \
 /* pid etc.. */	0,0,0,0, \
diff --git a/include/linux/shm.h b/include/linux/shm.h
index fdcdbb2..4328318 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -63,7 +63,7 @@
 #define SHM_IDX_MASK	((1<<_SHM_IDX_BITS)-1)
 #define SHM_READ_ONLY	(1<<(BITS_PER_PTR-1))
 
-#define SHMMAX 0x400000				/* max shared seg size (bytes) */
+#define SHMMAX 0x3fa000				/* max shared seg size (bytes) */
 #define SHMMIN 1	 /* really PAGE_SIZE */	/* min shared seg size (bytes) */
 #define SHMMNI (1<<_SHM_ID_BITS)		/* max num of segs system wide */
 #define SHMALL (1<<(_SHM_IDX_BITS+_SHM_ID_BITS))/* max shm system wide (pages) */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 9415bce..4e5fdbd 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -9,6 +9,11 @@
   char			sa_data[14];	/* 14 bytes of protocol address	*/
 };
 
+struct linger {
+  int 			l_onoff;	/* Linger active		*/
+  int			l_linger;	/* How long to linger for	*/
+};
+
 /* Socket types. */
 #define SOCK_STREAM	1		/* stream (connection) socket	*/
 #define SOCK_DGRAM	2		/* datagram (conn.less) socket	*/
diff --git a/include/linux/sockios.h b/include/linux/sockios.h
index 76b8d90..2090c33 100644
--- a/include/linux/sockios.h
+++ b/include/linux/sockios.h
@@ -61,6 +61,8 @@
 #define SIOCSIFMTU	0x8922		/* set MTU size			*/
 #define	SIOCGIFHWADDR	0x8923		/* get hardware address		*/
 #define	SIOCSIFHWADDR	0x8924		/* set hardware address (NI)	*/
+#define SIOCGIFENCAP	0x8925		/* get/set slip encapsulation   */
+#define SIOCSIFENCAP	0x8926		
 
 /* Routing table calls. */
 #define SIOCADDRT	0x8940		/* add routing table entry	*/
diff --git a/include/linux/string.h b/include/linux/string.h
index 62aa810..eacc6d1 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -366,7 +366,9 @@
 	"movsb\n\t"
 	"cld"
 	: /* no output */
-	:"c" (n),"S" (src+n-1),"D" (dest+n-1)
+	:"c" (n),
+	 "S" (n-1+(const char *)src),
+	 "D" (n-1+(char *)dest)
 	:"cx","si","di","memory");
 return dest;
 }
diff --git a/include/linux/sys.h b/include/linux/sys.h
index 55b99f5..da8ce5b 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -133,6 +133,8 @@
 extern int sys_old_syscall();
 extern int sys_modify_ldt();
 extern int sys_adjtimex();
+extern int sys_mprotect();
+extern int sys_sigprocmask();
 
 /*
  * These are system calls that will be removed at some time
@@ -141,10 +143,13 @@
 #ifdef notdef
 #define sys_waitpid	sys_old_syscall	/* sys_wait4	*/
 #define sys_olduname	sys_old_syscall /* sys_newuname	*/
+#define sys_uname	sys_old_syscall /* sys_newuname	*/
 #define sys_stat	sys_old_syscall /* sys_newstat	*/
 #define sys_fstat	sys_old_syscall	/* sys_newfstat	*/
 #define sys_lstat	sys_old_syscall /* sys_newlstat	*/
 #define sys_signal	sys_old_syscall	/* sys_sigaction */
+#define sys_sgetmask	sys_old_syscall /* sys_sigprocmask */
+#define sys_ssetmask	sys_old_syscall /* sig_sigprocmask */
 #endif
 
 typedef int (*fn_ptr)();
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 091576c..1fcb100 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -25,7 +25,6 @@
 #ifndef _LINUX_TIMEX_H
 #define _LINUX_TIMEX_H
 
-#include <sys/syscall.h>
 #include <linux/unistd.h>
 
 /*
@@ -95,12 +94,13 @@
 /*
  * Mode codes (timex.mode) 
  */
-#define ADJ_OFFSET	0x0001	/* time offset */
-#define ADJ_FREQUENCY	0x0002	/* frequency offset */
-#define ADJ_MAXERROR	0x0004	/* maximum time error */
-#define ADJ_ESTERROR	0x0008	/* estimated time error */
-#define ADJ_STATUS	0x0010	/* clock status */
-#define ADJ_TIMECONST	0x0020	/* pll time constant */
+#define ADJ_OFFSET		0x0001	/* time offset */
+#define ADJ_FREQUENCY		0x0002	/* frequency offset */
+#define ADJ_MAXERROR		0x0004	/* maximum time error */
+#define ADJ_ESTERROR		0x0008	/* estimated time error */
+#define ADJ_STATUS		0x0010	/* clock status */
+#define ADJ_TIMECONST		0x0020	/* pll time constant */
+#define ADJ_OFFSET_SINGLESHOT	0x8001	/* old-fashioned adjtime */
 
 /*
  * Clock command/status codes (timex.status)
@@ -111,55 +111,7 @@
 #define TIME_OOP	3	/* leap second in progress */
 #define TIME_BAD	4	/* clock not synchronized */
 
-#ifndef __KERNEL__
-/* This is used only by one or two programs so it isn't worth putting it into
- * a library; the functions are small enough to be substituted inline.
- */
-inline static 
-_syscall1(int, adjtimex, struct timex *, ntx);
-
-inline static int
-adjtime(struct timeval * itv, struct timeval * otv)
-{
-  struct timex tntx;
-  int result;
-
-  tntx.mode = 0;
-  if (itv)
-    {
-      tntx.offset = itv->tv_usec;
-      tntx.mode = ADJ_OFFSET;
-    }
-  result = adjtimex(&tntx);
-  if (result > 0) result = 0;
-
-  if (otv)
-    {
-      otv->tv_usec = tntx.offset;
-      otv->tv_sec = 0;
-    }
-  return result;
-}
-
-struct ntptimeval {
-	struct timeval time;	/* current time */
-	long maxerror;		/* maximum error (usec) */
-	long esterror;		/* estimated error (usec) */
-};
-
-inline static int
-ntp_gettime(struct ntptimeval * ntv)
-{
-  struct timex tntx;
-  int result;
-  result = adjtimex(&tntx);
-  ntv->time = tntx.time;
-  ntv->maxerror = tntx.maxerror;
-  ntv->esterror = tntx.esterror;
-  return result;
-}
-
-#else
+#ifdef __KERNEL__
 /*
  * kernel variables
  */
@@ -184,4 +136,5 @@
 
 extern long time_adjust;	/* The amount of adjtime left */
 #endif /* KERNEL */
+
 #endif /* LINUX_TIMEX_H */
diff --git a/include/linux/tpqic02.h b/include/linux/tpqic02.h
index bed2e58..56f676d 100644
--- a/include/linux/tpqic02.h
+++ b/include/linux/tpqic02.h
@@ -48,15 +48,13 @@
  *
  * Make sure you have the I/O ports/DMA channels 
  * and IRQ stuff configured properly!
- * NOTE: There may be other device drivers using the same major
- *       number. This must be avoided. Check for timer.h conflicts too.
+ * NOTE: Check for conflicts with TAPE_QIC02_TIMER in timer.h.
  */
 
 #define TAPE_QIC02_DRIVE	MT_ISQIC02_ALL_FEATURES	/* drive type */
 /* #define TAPE_QIC02_DRIVE	MT_ISWT5150 */
 #define TAPE_QIC02_IFC		WANGTEK		/* interface card type */
 /* #define TAPE_QIC02_IFC		ARCHIVE */
-#define TAPE_QIC02_MAJOR	12	/* major device number. /dev/loop seems to use 12 as well :-( */
 #define TAPE_QIC02_PORT 	0x300	/* controller port adress */
 #define TAPE_QIC02_IRQ		5	/* Muhammad, please don't use 2 here. -- Hennus */
 #define TAPE_QIC02_DMA		1	/* either 1 or 3, because 2 is used by the floppy */
diff --git a/include/linux/tty.h b/include/linux/tty.h
index a54bbe4..0adc292 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -242,6 +242,7 @@
 	struct tty_queue write_q;
 	struct tty_queue secondary;
 	struct wait_queue * except_q;
+	void *disc_data;
 };
 
 struct tty_ldisc {
@@ -368,8 +369,6 @@
 extern int is_orphaned_pgrp(int pgrp);
 extern int is_ignored(int sig);
 extern int tty_signal(int sig, struct tty_struct *tty);
-extern int kill_pg(int pgrp, int sig, int priv);
-extern int kill_sl(int sess, int sig, int priv);
 extern void tty_hangup(struct tty_struct * tty);
 extern void tty_vhangup(struct tty_struct * tty);
 extern void tty_unhangup(struct file *filp);
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
index e500b5c..87aee79 100644
--- a/include/linux/unistd.h
+++ b/include/linux/unistd.h
@@ -123,7 +123,7 @@
 #define __NR_wait4		114
 #define __NR_swapoff		115
 #define __NR_sysinfo		116
-#define __NR_ipc		117	/* not implemented yet */
+#define __NR_ipc		117
 #define __NR_fsync		118
 #define __NR_sigreturn		119
 #define __NR_clone		120
@@ -132,6 +132,7 @@
 #define __NR_modify_ldt		123
 #define __NR_adjtimex		124
 #define __NR_mprotect		125
+#define __NR_sigprocmask	126
 
 extern int errno;
 
diff --git a/init/main.c b/init/main.c
index 349020e..850e4d9 100644
--- a/init/main.c
+++ b/init/main.c
@@ -65,15 +65,19 @@
 
 static char printbuf[1024];
 
+extern int console_loglevel;
+
 extern char empty_zero_page[PAGE_SIZE];
 extern int vsprintf(char *,const char *,va_list);
 extern void init(void);
 extern void init_IRQ(void);
+extern long kmalloc_init (long,long);
 extern long blk_dev_init(long,long);
 extern long chr_dev_init(long,long);
 extern void floppy_init(void);
 extern void sock_init(void);
 extern long rd_init(long mem_start, int length);
+unsigned long net_dev_init(unsigned long, unsigned long);
 extern unsigned long simple_strtoul(const char *,char **,unsigned int);
 
 extern void hd_setup(char *str, int *ints);
@@ -223,7 +227,7 @@
 				 "r" (ticks),
 				 "0" (loops_per_sec)
 				:"dx");
-			printk("ok - %d.%02d BogoMips (tm)\n",
+			printk("ok - %lu.%02lu BogoMips (tm)\n",
 				loops_per_sec/500000,
 				(loops_per_sec/5000) % 100);
 			return;
@@ -281,6 +285,8 @@
 			root_mountflags |= MS_RDONLY;
 		else if (!strcmp(line,"rw"))
 			root_mountflags &= ~MS_RDONLY;
+		else if (!strcmp(line,"debug"))
+			console_loglevel = 10;
 		else if (!strcmp(line,"no387")) {
 			hard_math = 0;
 			__asm__("movl %%cr0,%%eax\n\t"
@@ -357,8 +363,12 @@
 	prof_len >>= 2;
 	memory_start += prof_len * sizeof(unsigned long);
 #endif
+	memory_start = kmalloc_init(memory_start,memory_end);
 	memory_start = chr_dev_init(memory_start,memory_end);
 	memory_start = blk_dev_init(memory_start,memory_end);
+#ifdef CONFIG_INET
+	memory_start = net_dev_init(memory_start,memory_end);
+#endif
 	sti();
 	calibrate_delay();
 #ifdef CONFIG_SCSI
diff --git a/ipc/Makefile b/ipc/Makefile
index b2f237b..a3a18a7 100644
--- a/ipc/Makefile
+++ b/ipc/Makefile
@@ -25,9 +25,6 @@
 ipc.o: $(OBJS) 
 	$(LD) -r -o ipc.o $(OBJS) 
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M $(SRCS) > .depend
 
diff --git a/ipc/shm.c b/ipc/shm.c
index 48723d5..4790a6a 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -682,17 +682,17 @@
 	for (shmd = shp->attaches; shmd; shmd = shmd->seg_next) {
 		unsigned long tmp, *pte;
 		if ((shmd->shm_sgn >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
-			printk ("shm_swap: id=%d does not match shmd\n", id);
+			printk ("shm_swap: id=%ld does not match shmd\n", id);
 			continue;
 		}
 		tmp = shmd->start + (idx << PAGE_SHIFT);
 		if (tmp >= shmd->end) {
-			printk ("shm_swap: too large idx=%d id=%d PANIC\n",idx, id);
+			printk ("shm_swap: too large idx=%ld id=%ld PANIC\n",idx, id);
 			continue;
 		}
 		pte = PAGE_DIR_OFFSET(shmd->task->tss.cr3,tmp);
 		if (!(*pte & 1)) { 
-			printk("shm_swap: bad pgtbl! id=%d start=%x idx=%d\n", 
+			printk("shm_swap: bad pgtbl! id=%ld start=%lx idx=%ld\n", 
 					id, shmd->start, idx);
 			*pte = 0;
 			continue;
diff --git a/kernel/Makefile b/kernel/Makefile
index 9473e32..0479a04 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -34,9 +34,6 @@
 sched.o: sched.c
 	$(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $<
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/kernel/exit.c b/kernel/exit.c
index 7ba3116..9bc7316 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -348,7 +348,7 @@
 	}
 }
 
-volatile void do_exit(long code)
+NORET_TYPE void do_exit(long code)
 {
 	struct task_struct *p;
 	int i;
diff --git a/kernel/fork.c b/kernel/fork.c
index ba153dc..44d6e01 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -117,7 +117,7 @@
 /*
  *  Ok, this is the main fork-routine. It copies the system process
  * information (task[nr]) and sets up the necessary registers. It
- * also copies the data segment in it's entirety.
+ * also copies the data segment in its entirety.
  */
 asmlinkage int sys_fork(struct pt_regs regs)
 {
diff --git a/kernel/panic.c b/kernel/panic.c
index ee91d22..43ba3c6 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -17,7 +17,7 @@
 
 extern int vsprintf(char * buf, const char * fmt, va_list args);
 
-volatile void panic(const char * fmt, ...)
+NORET_TYPE void panic(const char * fmt, ...)
 {
 	static char buf[1024];
 	va_list args;
diff --git a/kernel/printk.c b/kernel/printk.c
index 491cd7d..e2d4276 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -27,15 +27,17 @@
 extern int vsprintf(char * buf, const char * fmt, va_list args);
 extern void console_print(const char *);
 
-#define DEFAULT_LOGLEVEL 7 /* anything more serious than KERN_DEBUG */
+#define DEFAULT_MESSAGE_LOGLEVEL 7 /* KERN_DEBUG */
+#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything more serious than KERN_DEBUG */
+
+unsigned long log_size = 0;
+struct wait_queue * log_wait = NULL;
+int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
 
 static void (*console_print_proc)(const char *) = 0;
 static char log_buf[LOG_BUF_LEN];
 static unsigned long log_start = 0;
 static unsigned long logged_chars = 0;
-static int console_loglevel = DEFAULT_LOGLEVEL;
-unsigned long log_size = 0;
-struct wait_queue * log_wait = NULL;
 
 /*
  * Commands to sys_syslog:
@@ -55,6 +57,7 @@
 	unsigned long i, j, count;
 	int do_clear = 0;
 	char c;
+	int error;
 
 	if ((type != 3) && !suser())
 		return -EPERM;
@@ -68,38 +71,40 @@
 				return -EINVAL;
 			if (!len)
 				return 0;
-			verify_area(VERIFY_WRITE,buf,len);
+			error = verify_area(VERIFY_WRITE,buf,len);
+			if (error)
+				return error;
+			cli();
 			while (!log_size) {
-				if (current->signal & ~current->blocked)
+				if (current->signal & ~current->blocked) {
+					sti();
 					return -ERESTARTSYS;
-				cli();
-				if (!log_size)
+				}
 					interruptible_sleep_on(&log_wait);
-				sti();
 			}
 			i = 0;
-			cli();
 			while (log_size && i < len) {
-				cli();
 				c = *((char *) log_buf+log_start);
 				log_start++;
 				log_size--;
 				log_start &= LOG_BUF_LEN-1;
-				sti();
 				put_fs_byte(c,buf);
 				buf++;
 				i++;
 			}
 			sti();
 			return i;
-		case 4:		/* Read/clear last 4k of kernel messages */
+		case 4:		/* Read/clear last kernel messages */
 			do_clear = 1; 
-		case 3:		/* Read last 4k of kernel messages */
+			/* FALL THRU */
+		case 3:		/* Read last kernel messages */
 			if (!buf || len < 0)
 				return -EINVAL;
 			if (!len)
 				return 0;
-			verify_area(VERIFY_WRITE,buf,len);
+			error = verify_area(VERIFY_WRITE,buf,len);
+			if (error)
+				return error;
 			count = len;
 			if (count > LOG_BUF_LEN)
 				count = LOG_BUF_LEN;
@@ -120,7 +125,7 @@
 			console_loglevel = 1; /* only panic messages shown */
 			return 0;
 		case 7:		/* Enable logging to console */
-			console_loglevel = DEFAULT_LOGLEVEL;
+			console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
 			return 0;
 		case 8:
 			if (len < 0 || len > 8)
@@ -138,7 +143,10 @@
 	int i;
 	char *msg, *p, *buf_end;
 	static char msg_level = -1;
+	long flags;
 
+	save_flags(flags);
+	cli();
 	va_start(args, fmt);
 	i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */
 	buf_end = buf + 3 + i;
@@ -154,7 +162,7 @@
 			) {
 				p -= 3;
 				p[0] = '<';
-				p[1] = DEFAULT_LOGLEVEL - 1 + '0';
+				p[1] = DEFAULT_MESSAGE_LOGLEVEL - 1 + '0';
 				p[2] = '>';
 			} else
 				msg += 3;
@@ -179,6 +187,7 @@
 		if (*p == '\n')
 			msg_level = -1;
 	}
+	restore_flags(flags);
 	wake_up_interruptible(&log_wait);
 	return i;
 }
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4e5d87b..b6a673b 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -111,11 +111,15 @@
  * tables. NOTE! You should check that the long isn't on a page boundary,
  * and that it is in the task area before calling this: this routine does
  * no checking.
+ *
+ * Now keeps R/W state of page so that a text page stays readonly
+ * even if a debugger scribbles breakpoints into it.  -M.U-
  */
 static void put_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long data)
 {
-	unsigned long page, pte;
+	unsigned long page, pte = 0;
+	int readonly = 0;
 
 repeat:
 	page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
@@ -126,10 +130,12 @@
 		page = *((unsigned long *) page);
 	}
 	if (!(page & PAGE_PRESENT)) {
-		do_no_page(PAGE_RW,addr,tsk,0);
+		do_no_page(0 /* PAGE_RW */ ,addr,tsk,0);
 		goto repeat;
 	}
 	if (!(page & PAGE_RW)) {
+		if(!(page & PAGE_COW))
+			readonly = 1;
 		do_wp_page(PAGE_RW | PAGE_PRESENT,addr,tsk,0);
 		goto repeat;
 	}
@@ -138,6 +144,10 @@
 	page &= PAGE_MASK;
 	page += addr & ~PAGE_MASK;
 	*(unsigned long *) page = data;
+	if(readonly) {
+		*(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
+		invalidate();
+	} 
 }
 
 /*
@@ -258,8 +268,10 @@
 	}
 	if (!(child->flags & PF_PTRACED))
 		return -ESRCH;
-	if (child->state != TASK_STOPPED && request != PTRACE_DETACH)
-		return -ESRCH;
+	if (child->state != TASK_STOPPED) {
+		if (request != PTRACE_KILL && request != PTRACE_DETACH)
+			return -ESRCH;
+	}
 	if (child->p_pptr != current)
 		return -ESRCH;
 
diff --git a/kernel/sched.c b/kernel/sched.c
index 1b95bcb..5ffbc7e 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -23,6 +23,7 @@
 #include <linux/ptrace.h>
 #include <linux/segment.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -125,7 +126,7 @@
 sys_newfstat, sys_uname, sys_iopl, sys_vhangup, sys_idle, sys_vm86,
 sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn,
 sys_clone, sys_setdomainname, sys_newuname, sys_modify_ldt,
-sys_adjtimex};
+sys_adjtimex, sys_mprotect, sys_sigprocmask };
 
 /* So we don't have to do any more manual updating.... */
 int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
@@ -206,7 +207,7 @@
 			p->state = TASK_RUNNING;
 			continue;
 		}
-		if (p->timeout && p->timeout < jiffies) {
+		if (p->timeout && p->timeout <= jiffies) {
 			p->timeout = 0;
 			p->state = TASK_RUNNING;
 		}
@@ -280,7 +281,7 @@
 			}
 		}
 		if (!tmp->next) {
-			printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
+			printk("wait_queue is bad (eip = %08lx)\n",((unsigned long *) q)[-1]);
 			printk("        q = %p\n",q);
 			printk("       *q = %p\n",*q);
 			printk("      tmp = %p\n",tmp);
@@ -306,7 +307,7 @@
 			}
 		}
 		if (!tmp->next) {
-			printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
+			printk("wait_queue is bad (eip = %08lx)\n",((unsigned long *) q)[-1]);
 			printk("        q = %p\n",q);
 			printk("       *q = %p\n",*q);
 			printk("      tmp = %p\n",tmp);
@@ -414,7 +415,9 @@
 	unsigned long nr = 0;
 
 	for(p = &LAST_TASK; p > &FIRST_TASK; --p)
-		if (*p && (*p)->state == TASK_RUNNING)
+		if (*p && ((*p)->state == TASK_RUNNING ||
+			   (*p)->state == TASK_UNINTERRUPTIBLE ||
+			   (*p)->state == TASK_SWAPPING))
 			nr += FIXED_1;
 	return nr;
 }
@@ -445,31 +448,31 @@
  */
 static void second_overflow(void)
 {
-        long ltemp;
+	long ltemp;
 	/* last time the cmos clock got updated */
 	static long last_rtc_update=0;
 	extern int set_rtc_mmss(unsigned long);
 
 	/* Bump the maxerror field */
-        time_maxerror = (0x70000000-time_maxerror < time_tolerance) ?
+	time_maxerror = (0x70000000-time_maxerror < time_tolerance) ?
 	  0x70000000 : (time_maxerror + time_tolerance);
 
 	/* Run the PLL */
-        if (time_offset < 0) {
-                ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1;
-                time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+	if (time_offset < 0) {
+		ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1;
+		time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
 		time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
 		time_adj = - time_adj;
-        } else if (time_offset > 0) {
-                ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1;
-                time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+	} else if (time_offset > 0) {
+		ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1;
+		time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
 		time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
-        } else {
+	} else {
 		time_adj = 0;
 	}
 
-        time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE))
-            + FINETUNE;
+	time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE))
+	    + FINETUNE;
 
 	/* Handle the leap second stuff */
 	switch (time_status) {
@@ -500,6 +503,25 @@
 	    last_rtc_update = xtime.tv_sec;
 }
 
+static int lost_ticks = 0;
+
+/*
+ * disregard lost ticks for now.. We don't care enough.
+ */
+static void timer_bh(void * unused)
+{
+	cli();
+	while (next_timer && next_timer->expires == 0) {
+		void (*fn)(unsigned long) = next_timer->function;
+		unsigned long data = next_timer->data;
+		next_timer = next_timer->next;
+		sti();
+		fn(data);
+		cli();
+	}
+	sti();
+}
+
 /*
  * The int argument is really a (struct pt_regs *), in case the
  * interrupt wants to know from where it was called. The timer
@@ -512,23 +534,23 @@
 	struct timer_struct *tp = timer_table+0;
 	struct task_struct * task_p;
 
-        long ltemp;
+	long ltemp;
 
-        /* Advance the phase, once it gets to one microsecond, then
-         * advance the tick more.
-         */
-        time_phase += time_adj;
-        if (time_phase < -FINEUSEC) {
-                ltemp = -time_phase >> SHIFT_SCALE;
-                time_phase += ltemp << SHIFT_SCALE;
-                xtime.tv_usec += tick - ltemp;
-        }
-        else if (time_phase > FINEUSEC) {
-                ltemp = time_phase >> SHIFT_SCALE;
-                time_phase -= ltemp << SHIFT_SCALE;
-                xtime.tv_usec += tick + ltemp;
-        } else
-                xtime.tv_usec += tick;
+	/* Advance the phase, once it gets to one microsecond, then
+	 * advance the tick more.
+	 */
+	time_phase += time_adj;
+	if (time_phase < -FINEUSEC) {
+		ltemp = -time_phase >> SHIFT_SCALE;
+		time_phase += ltemp << SHIFT_SCALE;
+		xtime.tv_usec += tick - ltemp;
+	}
+	else if (time_phase > FINEUSEC) {
+		ltemp = time_phase >> SHIFT_SCALE;
+		time_phase -= ltemp << SHIFT_SCALE;
+		xtime.tv_usec += tick + ltemp;
+	} else
+		xtime.tv_usec += tick;
 
 	if (time_adjust)
 	{
@@ -614,16 +636,16 @@
 		sti();
 	}
 	cli();
-	while (next_timer && next_timer->expires == 0) {
-		void (*fn)(unsigned long) = next_timer->function;
-		unsigned long data = next_timer->data;
-		next_timer = next_timer->next;
-		sti();
-		fn(data);
-		cli();
+	if (next_timer) {
+		if (next_timer->expires) {
+			next_timer->expires--;
+			if (!next_timer->expires)
+				mark_bh(TIMER_BH);
+		} else {
+			lost_ticks++;
+			mark_bh(TIMER_BH);
+		}
 	}
-	if (next_timer)
-		next_timer->expires--;
 	sti();
 }
 
@@ -645,7 +667,7 @@
 
 asmlinkage int sys_getppid(void)
 {
-	return current->p_pptr->pid;
+	return current->p_opptr->pid;
 }
 
 asmlinkage int sys_getuid(void)
@@ -685,25 +707,27 @@
 
 static void show_task(int nr,struct task_struct * p)
 {
-	int i, j;
-	unsigned char * stack;
+	static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" };
 
-	printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid,
-		p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
-	i = 0;
-	j = PAGE_SIZE;
-	if (!(stack = (unsigned char *) p->kernel_stack_page)) {
-		stack = (unsigned char *) init_kernel_stack;
-		j = sizeof(init_kernel_stack);
-	}
-	while (i<j && !*(stack++))
-		i++;
-	printk("%d/%d chars free in kstack\n",i,j);
-	printk("   PC=%08X.", *(1019 + (unsigned long *) p));
-	if (p->p_ysptr || p->p_osptr) 
-		printk("   Younger sib=%d, older sib=%d\n", 
-			p->p_ysptr ? p->p_ysptr->pid : -1,
-			p->p_osptr ? p->p_osptr->pid : -1);
+	printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr);
+	if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *))
+		printk(stat_nam[p->state]);
+	else
+		printk(" ");
+	/* this prints bogus values for the current process */
+	printk(" %08lX ", ((unsigned long *)p->tss.esp)[2]);
+	printk("%5lu %5d %6d ",
+		p->tss.esp - p->kernel_stack_page, p->pid, p->p_pptr->pid);
+	if (p->p_cptr)
+		printk("%5d ", p->p_cptr->pid);
+	else
+		printk("      ");
+	if (p->p_ysptr)
+		printk("%7d", p->p_ysptr->pid);
+	else
+		printk("       ");
+	if (p->p_osptr)
+		printk(" %5d\n", p->p_osptr->pid);
 	else
 		printk("\n");
 }
@@ -712,7 +736,8 @@
 {
 	int i;
 
-	printk("Task-info:\n");
+	printk("                         free                        sibling\n");
+	printk("  task             PC    stack   pid father child younger older\n");
 	for (i=0 ; i<NR_TASKS ; i++)
 		if (task[i])
 			show_task(i,task[i]);
@@ -723,6 +748,7 @@
 	int i;
 	struct desc_struct * p;
 
+	bh_base[TIMER_BH].routine = timer_bh;
 	if (sizeof(struct sigaction) != 16)
 		panic("Struct sigaction MUST be 16 bytes");
 	set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
diff --git a/kernel/signal.c b/kernel/signal.c
index b84ce35..4ed60cd 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -47,6 +47,39 @@
 	unsigned long cr2;
 };
 
+asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)
+{
+	sigset_t new_set, old_set = current->blocked;
+	int error;
+
+	if (set) {
+		error = verify_area(VERIFY_READ, set, sizeof(sigset_t));
+		if (error)
+			return error;
+		new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE;
+		switch (how) {
+		case SIG_BLOCK:
+			current->blocked |= new_set;
+			break;
+		case SIG_UNBLOCK:
+			current->blocked &= ~new_set;
+			break;
+		case SIG_SETMASK:
+			current->blocked = new_set;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	if (oset) {
+		error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t));
+		if (error)
+			return error;
+		put_fs_long(old_set, (unsigned long *) oset);
+	}
+	return 0;
+}
+
 asmlinkage int sys_sgetmask(void)
 {
 	return current->blocked;
@@ -245,7 +278,7 @@
 	put_fs_long(0,frame+21);		/* 387 state pointer - not implemented*/
 /* non-iBCS2 extensions.. */
 	put_fs_long(oldmask, frame+22);
-	put_fs_long(0, frame+23);		/* cr2 - not implemented */
+	put_fs_long(current->tss.cr2, frame+23);
 /* set up the return code... */
 	put_fs_long(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
 	put_fs_long(0x80cd0000, CODE(4));	/* int $0x80 */
@@ -312,6 +345,8 @@
 				continue;
 
 			case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+				if (current->flags & PF_PTRACED)
+					continue;
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
 				if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
diff --git a/kernel/sys.c b/kernel/sys.c
index 45e33c8..315925b 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -27,7 +27,6 @@
  */
 static int C_A_D = 1;
 
-extern int session_of_pgrp(int pgrp);
 extern void adjust_clock(void);
 
 #define	PZERO	15
@@ -477,6 +476,7 @@
 	for (i=0 ; i<NR_TASKS ; i++)
 		if (task[i] && (task[i]->pid == pid) &&
 		    ((task[i]->p_pptr == current) || 
+		     (task[i]->p_opptr == current) || 
 		     (task[i] == current))) {
 			if (task[i]->leader)
 				return -EPERM;
diff --git a/kernel/time.c b/kernel/time.c
index f7b20fe..0542096 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -348,27 +348,32 @@
 		time_constant = txc.time_constant;
 
 	    if (txc.mode & ADJ_OFFSET)
-	    {
-		time_offset = txc.offset << SHIFT_UPDATE;
-		mtemp = xtime.tv_sec - time_reftime;
-		time_reftime = xtime.tv_sec;
-		if (mtemp > (MAXSEC+2) || mtemp < 0)
+	      if (txc.mode == ADJ_OFFSET_SINGLESHOT)
+		{
+		  time_adjust = txc.offset;
+		}
+	      else /* XXX should give an error if other bits set */
+		{
+		  time_offset = txc.offset << SHIFT_UPDATE;
+		  mtemp = xtime.tv_sec - time_reftime;
+		  time_reftime = xtime.tv_sec;
+		  if (mtemp > (MAXSEC+2) || mtemp < 0)
 		    mtemp = 0;
 
-		if (txc.offset < 0)
+		  if (txc.offset < 0)
 		    time_freq -= (-txc.offset * mtemp) >>
-			(time_constant + time_constant);
-		else
+		      (time_constant + time_constant);
+		  else
 		    time_freq += (txc.offset * mtemp) >>
-			(time_constant + time_constant);
+		      (time_constant + time_constant);
 
-		ltemp = time_tolerance << SHIFT_KF;
+		  ltemp = time_tolerance << SHIFT_KF;
 
-		if (time_freq > ltemp)
+		  if (time_freq > ltemp)
 		    time_freq = ltemp;
-		else if (time_freq < -ltemp)
+		  else if (time_freq < -ltemp)
 		    time_freq = -ltemp;
-	    }
+		}
 	}
 	txc.offset	   = save_adjust;
 	txc.frequency	   = time_freq;
diff --git a/kernel/traps.c b/kernel/traps.c
index 111b443..420ff31 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -67,11 +67,11 @@
 	if ((regs->eflags & VM_MASK) || (3 & regs->cs) == 3)
 		return;
 
-	printk("%s: %04x\n", str, err & 0xffff);
-	printk("EIP:    %04x:%08x\nEFLAGS: %08x\n", 0xffff & regs->cs,regs->eip,regs->eflags);
-	printk("eax: %08x   ebx: %08x   ecx: %08x   edx: %08x\n",
+	printk("%s: %04lx\n", str, err & 0xffff);
+	printk("EIP:    %04x:%08lx\nEFLAGS: %08lx\n", 0xffff & regs->cs,regs->eip,regs->eflags);
+	printk("eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
 		regs->eax, regs->ebx, regs->ecx, regs->edx);
-	printk("esi: %08x   edi: %08x   ebp: %08x\n",
+	printk("esi: %08lx   edi: %08lx   ebp: %08lx\n",
 		regs->esi, regs->edi, regs->ebp);
 	printk("ds: %04x   es: %04x   fs: %04x   gs: %04x\n",
 		regs->ds, regs->es, regs->fs, regs->gs);
diff --git a/kernel/vsprintf.c b/kernel/vsprintf.c
index 8cb121f..ef449a2 100644
--- a/kernel/vsprintf.c
+++ b/kernel/vsprintf.c
@@ -195,6 +195,8 @@
 
 		case 's':
 			s = va_arg(args, char *);
+			if (!s)
+				s = "<NULL>";
 			len = strlen(s);
 			if (precision < 0)
 				precision = len;
diff --git a/lib/Makefile b/lib/Makefile
index af32e9a..d6b77ea 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -14,15 +14,12 @@
 	$(CC) $(CFLAGS) -c $<
 
 OBJS  = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \
-	execve.o wait.o string.o malloc.o
+	execve.o wait.o string.o
 
 lib.a: $(OBJS)
 	$(AR) rcs lib.a $(OBJS)
 	sync
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/mm/Makefile b/mm/Makefile
index 510861c..37bad38 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -14,14 +14,11 @@
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 
-OBJS	= memory.o swap.o mmap.o vmalloc.o
+OBJS	= memory.o swap.o mmap.o kmalloc.o vmalloc.o
 
 mm.o: $(OBJS)
 	$(LD) -r -o mm.o $(OBJS)
 
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/mm/kmalloc.c b/mm/kmalloc.c
new file mode 100644
index 0000000..e75fa20
--- /dev/null
+++ b/mm/kmalloc.c
@@ -0,0 +1,338 @@
+/*
+ *  linux/mm/kmalloc.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds & Roger Wolff.
+ *
+ *  Written by R.E. Wolff Sept/Oct '93.
+ *
+ */
+
+#include <linux/mm.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+
+#define GFP_LEVEL_MASK 0xf
+
+/* I want this low enough for a while to catch errors.
+   I want this number to be increased in the near future:
+        loadable device drivers should use this function to get memory */
+
+#define MAX_KMALLOC_K 4
+
+
+/* This defines how many times we should try to allocate a free page before
+   giving up. Normally this shouldn't happen at all. */
+#define MAX_GET_FREE_PAGE_TRIES 4
+
+
+/* Private flags. */
+
+#define MF_USED 0xffaa0055
+#define MF_FREE 0x0055ffaa
+
+
+/* 
+ * Much care has gone into making these routines in this file reentrant.
+ *
+ * The fancy bookkeeping of nbytesmalloced and the like are only used to
+ * report them to the user (oooohhhhh, aaaaahhhhh....) are not 
+ * protected by cli(). (If that goes wrong. So what?)
+ *
+ * These routines restore the interrupt status to allow calling with ints
+ * off. 
+ */
+
+/* 
+ * A block header. This is in front of every malloc-block, whether free or not.
+ */
+struct block_header {
+	unsigned long bh_flags;
+	union {
+		unsigned long ubh_length;
+		struct block_header *fbh_next;
+	} vp;
+};
+
+
+#define bh_length vp.ubh_length
+#define bh_next   vp.fbh_next
+#define BH(p) ((struct block_header *)(p))
+
+
+/* 
+ * The page descriptor is at the front of every page that malloc has in use. 
+ */
+struct page_descriptor {
+	struct page_descriptor *next;
+	struct block_header *firstfree;
+	int order;
+	int nfree;
+};
+
+
+#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK))
+
+
+/*
+ * A size descriptor describes a specific class of malloc sizes.
+ * Each class of sizes has its own freelist.
+ */
+struct size_descriptor {
+	struct page_descriptor *firstfree;
+	int size;
+	int nblocks;
+
+	int nmallocs;
+	int nfrees;
+	int nbytesmalloced;
+	int npages;
+};
+
+
+struct size_descriptor sizes[] = { 
+	{ NULL,  32,127, 0,0,0,0 },
+	{ NULL,  64, 63, 0,0,0,0 },
+	{ NULL, 128, 31, 0,0,0,0 },
+	{ NULL, 252, 16, 0,0,0,0 },
+	{ NULL, 508,  8, 0,0,0,0 },
+	{ NULL,1020,  4, 0,0,0,0 },
+	{ NULL,2040,  2, 0,0,0,0 },
+	{ NULL,4080,  1, 0,0,0,0 },
+	{ NULL,   0,  0, 0,0,0,0 }
+};
+
+
+#define NBLOCKS(order)          (sizes[order].nblocks)
+#define BLOCKSIZE(order)        (sizes[order].size)
+
+
+
+long kmalloc_init (long start_mem,long end_mem)
+{
+	int order;
+
+/* 
+ * Check the static info array. Things will blow up terribly if it's
+ * incorrect. This is a late "compile time" check.....
+ */
+for (order = 0;BLOCKSIZE(order);order++)
+    {
+    if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) >
+        PAGE_SIZE) 
+        {
+        printk ("Cannot use %d bytes out of %d in order = %d block mallocs\n",
+                NBLOCKS (order) * BLOCKSIZE(order) + 
+                        sizeof (struct page_descriptor),
+                (int) PAGE_SIZE,
+                BLOCKSIZE (order));
+        panic ("This only happens if someone messes with kmalloc");
+        }
+    }
+return start_mem;
+}
+
+
+
+int get_order (int size)
+{
+	int order;
+
+	/* Add the size of the header */
+	size += sizeof (struct block_header); 
+	for (order = 0;BLOCKSIZE(order);order++)
+		if (size <= BLOCKSIZE (order))
+			return order; 
+	return -1;
+}
+
+void * kmalloc (size_t size, int priority)
+{
+	unsigned long flags;
+	int order,tries,i,sz;
+	struct block_header *p;
+	struct page_descriptor *page;
+
+/* Sanity check... */
+if (size > MAX_KMALLOC_K * 1024) 
+     {
+     printk ("kmalloc: I refuse to allocate %d bytes (for now max = %d).\n",
+                size,MAX_KMALLOC_K*1024);
+     return (NULL);
+     }
+
+order = get_order (size);
+if (order < 0)
+    {
+    printk ("kmalloc of too large a block (%d bytes).\n",size);
+    return (NULL);
+    }
+
+save_flags(flags);
+
+/* It seems VERY unlikely to me that it would be possible that this 
+   loop will get executed more than once. */
+tries = MAX_GET_FREE_PAGE_TRIES; 
+while (tries --)
+    {
+    /* Try to allocate a "recently" freed memory block */
+    cli ();
+    if ((page = sizes[order].firstfree) &&
+        (p    =  page->firstfree))
+        {
+        if (p->bh_flags == MF_FREE)
+            {
+            page->firstfree = p->bh_next;
+            page->nfree--;
+            if (!page->nfree)
+                {
+                sizes[order].firstfree = page->next;
+                page->next = NULL;
+                }
+            restore_flags(flags);
+
+            sizes [order].nmallocs++;
+            sizes [order].nbytesmalloced += size;
+            p->bh_flags =  MF_USED; /* As of now this block is officially in use */
+            p->bh_length = size;
+            return p+1; /* Pointer arithmetic: increments past header */
+            }
+        printk ("Problem: block on freelist at %08lx isn't free.\n",(long)p);
+        return (NULL);
+        }
+    restore_flags(flags);
+
+
+    /* Now we're in trouble: We need to get a new free page..... */
+
+    sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */
+
+    /* This can be done with ints on: This is private to this invocation */
+    page = (struct page_descriptor *) __get_free_page (priority & GFP_LEVEL_MASK);
+    if (!page) 
+        {
+        printk ("Couldn't get a free page.....\n");
+        return NULL;
+        }
+#if 0
+    printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz);
+#endif
+    sizes[order].npages++;
+
+    /* Loop for all but last block: */
+    for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next) 
+        {
+        p->bh_flags = MF_FREE;
+        p->bh_next = BH ( ((long)p)+sz);
+        }
+    /* Last block: */
+    p->bh_flags = MF_FREE;
+    p->bh_next = NULL;
+
+    page->order = order;
+    page->nfree = NBLOCKS(order); 
+    page->firstfree = BH(page+1);
+#if 0
+    printk ("%d blocks per page\n",page->nfree);
+#endif
+    /* Now we're going to muck with the "global" freelist for this size:
+       this should be uniterruptible */
+    cli ();
+    /* 
+     * sizes[order].firstfree used to be NULL, otherwise we wouldn't be
+     * here, but you never know.... 
+     */
+    page->next = sizes[order].firstfree;
+    sizes[order].firstfree = page;
+    restore_flags(flags);
+    }
+
+/* Pray that printk won't cause this to happen again :-) */
+
+printk ("Hey. This is very funny. I tried %d times to allocate a whole\n"
+        "new page for an object only %d bytes long, but some other process\n"
+        "beat me to actually allocating it. Also note that this 'error'\n"
+        "message is soooo very long to catch your attention. I'd appreciate\n"
+        "it if you'd be so kind as to report what conditions caused this to\n"
+        "the author of this kmalloc: wolff@dutecai.et.tudelft.nl.\n"
+        "(Executive summary: This can't happen)\n", 
+                MAX_GET_FREE_PAGE_TRIES,
+                size);
+return NULL;
+}
+
+
+void kfree_s (void *ptr,int size)
+{
+unsigned long flags;
+int order;
+register struct block_header *p=((struct block_header *)ptr) -1;
+struct page_descriptor *page,*pg2;
+
+page = PAGE_DESC (p);
+order = page->order;
+if ((order < 0) || 
+    (order > sizeof (sizes)/sizeof (sizes[0])) ||
+    (((long)(page->next)) & ~PAGE_MASK) ||
+    (p->bh_flags != MF_USED))
+    {
+    printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d\n",
+                p, page->next, page->order);
+    return;
+    }
+if (size &&
+    size != p->bh_length)
+    {
+    printk ("Trying to free pointer at %p with wrong size: %d instead of %lu.\n",
+        p,size,p->bh_length);
+    return;
+    }
+size = p->bh_length;
+p->bh_flags = MF_FREE; /* As of now this block is officially free */
+
+save_flags(flags);
+cli ();
+p->bh_next = page->firstfree;
+page->firstfree = p;
+page->nfree ++;
+
+if (page->nfree == 1)
+   { /* Page went from full to one free block: put it on the freelist */
+   if (page->next)
+        {
+        printk ("Page %p already on freelist dazed and confused....\n", page);
+        }
+   else
+        {
+        page->next = sizes[order].firstfree;
+        sizes[order].firstfree = page;
+        }
+   }
+
+/* If page is completely free, free it */
+if (page->nfree == NBLOCKS (page->order))
+    {
+#if 0
+    printk ("Freeing page %08x.\n", (long)page);
+#endif
+    if (sizes[order].firstfree == page)
+        {
+        sizes[order].firstfree = page->next;
+        }
+    else
+        {
+        for (pg2=sizes[order].firstfree;
+                (pg2 != NULL) && (pg2->next != page);
+                        pg2=pg2->next)
+            /* Nothing */;
+        if (pg2 != NULL)
+            pg2->next = page->next;
+        else
+            printk ("Ooops. page %p doesn't show on freelist.\n", page);
+        }
+    free_page ((long)page);
+    }
+restore_flags(flags);
+
+sizes[order].nfrees++;      /* Noncritical (monitoring) admin stuff */
+sizes[order].nbytesmalloced -= size;
+}
diff --git a/mm/memory.c b/mm/memory.c
index 1ea52f1..98ef0d8 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -89,7 +89,7 @@
 		return;
 	*page_dir = 0;
 	if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
-		printk("Bad page table: [%p]=%08x\n",page_dir,pg_table);
+		printk("Bad page table: [%p]=%08lx\n",page_dir,pg_table);
 		return;
 	}
 	if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
@@ -335,7 +335,7 @@
 		mask |= ZERO_PAGE;
 	}
 	if (from & ~PAGE_MASK) {
-		printk("zeromap_page_range: from = %08x\n",from);
+		printk("zeromap_page_range: from = %08lx\n",from);
 		return -EINVAL;
 	}
 	dir = PAGE_DIR_OFFSET(current->tss.cr3,from);
@@ -397,7 +397,7 @@
 		}
 	}
 	if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) {
-		printk("remap_page_range: from = %08x, to=%08x\n",from,to);
+		printk("remap_page_range: from = %08lx, to=%08lx\n",from,to);
 		return -EINVAL;
 	}
 	dir = PAGE_DIR_OFFSET(current->tss.cr3,from);
@@ -475,7 +475,7 @@
 	if ((prot & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT)
 		printk("put_page: prot = %08x\n",prot);
 	if (page >= high_memory) {
-		printk("put_page: trying to put page %08x at %08x\n",page,address);
+		printk("put_page: trying to put page %08lx at %08lx\n",page,address);
 		return 0;
 	}
 	page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
@@ -509,9 +509,9 @@
 	unsigned long tmp, *page_table;
 
 	if (page >= high_memory)
-		printk("put_dirty_page: trying to put page %08x at %08x\n",page,address);
+		printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address);
 	if (mem_map[MAP_NR(page)] != 1)
-		printk("mem_map disagrees with %08x at %08x\n",page,address);
+		printk("mem_map disagrees with %08lx at %08lx\n",page,address);
 	page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
 	if (PAGE_PRESENT & *page_table)
 		page_table = (unsigned long *) (PAGE_MASK & *page_table);
@@ -595,12 +595,12 @@
 		free_page(new_page);
 	return;
 bad_wp_page:
-	printk("do_wp_page: bogus page at address %08x (%08x)\n",address,old_page);
+	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
 	*(unsigned long *) pte = BAD_PAGE | PAGE_SHARED;
 	send_sig(SIGKILL, tsk, 1);
 	goto end_wp_page;
 bad_wp_pagetable:
-	printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte);
+	printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte);
 	*pde = BAD_PAGETABLE | PAGE_TABLE;
 	send_sig(SIGKILL, tsk, 1);
 end_wp_page:
@@ -645,7 +645,7 @@
 		__do_wp_page(error_code, address, tsk, user_esp);
 		return;
 	}
-	printk("bad page directory entry %08x\n",page);
+	printk("bad page directory entry %08lx\n",page);
 	*pg_table = 0;
 }
 
@@ -850,13 +850,19 @@
 		return;
 	}
 	address &= 0xfffff000;
+	tmp = 0;
 	for (mpnt = tsk->mmap; mpnt != NULL; mpnt = mpnt->vm_next) {
 		if (address < mpnt->vm_start)
-			continue;
-		if (address >= mpnt->vm_end)
-			continue;
-		if (!mpnt->vm_ops || !mpnt->vm_ops->nopage)
 			break;
+		if (address >= mpnt->vm_end) {
+			tmp = mpnt->vm_end;
+			continue;
+		}
+		if (!mpnt->vm_ops || !mpnt->vm_ops->nopage) {
+			++tsk->min_flt;
+			get_empty_page(tsk,address);
+			return;
+		}
 		mpnt->vm_ops->nopage(error_code, mpnt, address);
 		return;
 	}
@@ -866,10 +872,13 @@
 		return;
 	if (address >= tsk->end_data && address < tsk->brk)
 		return;
-	if (address+8192 >= (user_esp & 0xfffff000) && 
-	    address <= tsk->start_stack)
+	if (mpnt && mpnt == tsk->stk_vma &&
+	    address - tmp > mpnt->vm_start - address &&
+	    tsk->rlim[RLIMIT_STACK].rlim_cur > mpnt->vm_end - address) {
+		mpnt->vm_start = address;
 		return;
-	current->tss.cr2 = address;
+	}
+	tsk->tss.cr2 = address;
 	send_sig(SIGSEGV,tsk,1);
 	return;
 }
@@ -883,7 +892,6 @@
 {
 	unsigned long address;
 	unsigned long user_esp = 0;
-	unsigned long stack_limit;
 	unsigned int bit;
 
 	/* get the address */
@@ -901,23 +909,6 @@
 			do_wp_page(error_code, address, current, user_esp);
 		else
 			do_no_page(error_code, address, current, user_esp);
-		if (!user_esp)
-			return;
-		stack_limit = current->rlim[RLIMIT_STACK].rlim_cur;
-		if (stack_limit >= RLIM_INFINITY ||
-		    stack_limit >= current->start_stack ||
-		    user_esp >= (current->start_stack - stack_limit)) {
-#if 0
-			if (current->stk_vma != NULL) {
-				if (current->stk_vma->vm_start > user_esp)
-					current->stk_vma->vm_start = user_esp & PAGE_MASK;
-			} else
-				printk("do_no_page: no stack segment\n");
-#endif
-		} else {
-			current->tss.cr2 = address;
-			send_sig(SIGSEGV, current, 1);
-		}
 		return;
 	}
 	if (error_code & PAGE_RW) {
@@ -933,7 +924,7 @@
 		pg0[0] = PAGE_SHARED;
 	} else
 		printk("Unable to handle kernel paging request");
-	printk(" at address %08x\n",address);
+	printk(" at address %08lx\n",address);
 	die_if_kernel("Oops", regs, error_code);
 	do_exit(SIGKILL);
 }
@@ -1030,11 +1021,14 @@
 	unsigned long address;
 
 /*
- * Physical page 0 is special: it's a "zero-page", and is guaranteed to
- * stay that way - it's write-protected and when there is a c-o-w, the
- * mm handler treats it specially.
+ * Physical page 0 is special; it's not touched by Linux since BIOS
+ * and SMM (for laptops with [34]86/SL chips) may need it.  It is read
+ * and write protected to detect null pointer references in the
+ * kernel.
  */
+#if 0
 	memset((void *) 0, 0, PAGE_SIZE);
+#endif
 	start_mem = PAGE_ALIGN(start_mem);
 	address = 0;
 	pg_dir = swapper_pg_dir;
@@ -1111,14 +1105,16 @@
 		nr_free_pages++;
 	}
 	tmp = nr_free_pages << PAGE_SHIFT;
-	printk("Memory: %dk/%dk available (%dk kernel code, %dk reserved, %dk data)\n",
+	printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
 		tmp >> 10,
 		end_mem >> 10,
 		codepages << (PAGE_SHIFT-10),
 		reservedpages << (PAGE_SHIFT-10),
 		datapages << (PAGE_SHIFT-10));
+/* test if the WP bit is honoured in supervisor mode */
 	pg0[0] = PAGE_READONLY;
-	*((char *) 0) = 0;		/* test if the WP bit is honoured in supervisor mode */
+	invalidate();
+	__asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory");
 	pg0[0] = 0;
 	invalidate();
 	return;
diff --git a/mm/mmap.c b/mm/mmap.c
index 11ed1a9..10e24df 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -182,14 +182,13 @@
 	    end <= area->vm_start || end > area->vm_end ||
 	    end < addr)
 	{
-		printk("unmap_fixup: area=%x-%x, unmap %x-%x!!\n",
+		printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
 		       area->vm_start, area->vm_end, addr, end);
 		return;
 	}
 
 	/* Unmapping the whole area */
-	if (addr == area->vm_start && end == area->vm_end)
-	{
+	if (addr == area->vm_start && end == area->vm_end) {
 		if (area->vm_ops && area->vm_ops->close)
 			area->vm_ops->close(area);
 		return;
@@ -198,8 +197,10 @@
 	/* Work out to one of the ends */
 	if (addr >= area->vm_start && end == area->vm_end)
 		area->vm_end = addr;
-	if (addr == area->vm_start && end <= area->vm_end)
+	if (addr == area->vm_start && end <= area->vm_end) {
+		area->vm_offset += (end - area->vm_start);
 		area->vm_start = end;
+	}
 
 	/* Unmapping a hole */
 	if (addr > area->vm_start && end < area->vm_end)
@@ -208,6 +209,7 @@
 		mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
 
 		*mpnt = *area;
+		mpnt->vm_offset += (end - area->vm_start);
 		mpnt->vm_start = end;
 		if (mpnt->vm_inode)
 			mpnt->vm_inode->i_count++;
@@ -368,7 +370,7 @@
 		     vmp->vm_start < mpnt->vm_end) ||
 		    (vmp->vm_end >= mpnt->vm_start &&
 		     vmp->vm_end < mpnt->vm_end))
-			printk("insert_vm_struct: ins area %x-%x in area %x-%x\n",
+			printk("insert_vm_struct: ins area %lx-%lx in area %lx-%lx\n",
 			       vmp->vm_start, vmp->vm_end,
 			       mpnt->vm_start, vmp->vm_end);
 	}
diff --git a/mm/swap.c b/mm/swap.c
index c885362..8adc9eb 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -138,7 +138,7 @@
 	}
 	p = type + swap_info;
 	if (offset >= p->max) {
-		printk("swap_free: weirness\n");
+		printk("swap_free: weirdness\n");
 		return 0;
 	}
 	if (!p->swap_map[offset]) {
@@ -166,7 +166,7 @@
 	p = & swap_info[type];
 	offset = SWP_OFFSET(entry);
 	if (offset >= p->max) {
-		printk("swap_free: weirness\n");
+		printk("swap_free: weirdness\n");
 		return;
 	}
 	if (!(p->flags & SWP_USED)) {
@@ -180,7 +180,7 @@
 	if (offset > p->highest_bit)
 		p->highest_bit = offset;
 	if (!p->swap_map[offset])
-		printk("swap_free: swap-space map bad (entry %08x)\n",entry);
+		printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
 	else
 		if (!--p->swap_map[offset])
 			nr_swap_pages++;
@@ -356,7 +356,7 @@
 	    if(mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
 		    continue;
 	    if(!(PAGE_PRESENT & pg_table)) {
-		    printk("swap_out: bad page-table at pg_dir[%d]: %08x\n",
+		    printk("swap_out: bad page-table at pg_dir[%d]: %08lx\n",
 			    table, pg_table);
 		    ((unsigned long *) p->tss.cr3)[table] = 0;
 		    continue;
@@ -529,8 +529,8 @@
 			}
 			return;
 		}
-		printk("Trying to free free memory (%08x): memory probabably corrupted\n",addr);
-		printk("PC = %08x\n",*(((unsigned long *)&addr)-1));
+		printk("Trying to free free memory (%08lx): memory probabably corrupted\n",addr);
+		printk("PC = %08lx\n",*(((unsigned long *)&addr)-1));
 		return;
 	}
 }
@@ -557,10 +557,10 @@
 				restore_flags(flag); \
 				return result; \
 			} \
-			printk("Free page %08x has mem_map = %d\n", \
+			printk("Free page %08lx has mem_map = %d\n", \
 				result,mem_map[MAP_NR(result)]); \
 		} else \
-			printk("Result = 0x%08x - memory map destroyed\n", result); \
+			printk("Result = 0x%08lx - memory map destroyed\n", result); \
 		queue = 0; \
 		nr = 0; \
 	} else if (nr) { \
@@ -792,7 +792,7 @@
 		error = -EINVAL;
 		goto bad_swap;
 	}
-	p->swap_map = vmalloc(p->max);
+	p->swap_map = (unsigned char *) vmalloc(p->max);
 	if (!p->swap_map) {
 		error = -ENOMEM;
 		goto bad_swap;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index c33ad8d..ad022e1 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -14,6 +14,7 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/malloc.h>
+#include <asm/segment.h>
 
 struct vm_struct {
 	unsigned long flags;
@@ -109,6 +110,7 @@
 			return -1;
 		nr -= i;
 		index = 0;
+		dindex++;
 	}
 	return 0;
 }
@@ -162,3 +164,29 @@
 	}
 	return addr;
 }
+
+int vread(char *buf, char *addr, int count)
+{
+	struct vm_struct **p, *tmp;
+	char *vaddr, *buf_start = buf;
+	int n;
+
+	for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
+		vaddr = (char *) tmp->addr;
+		while (addr < vaddr) {
+			if (count == 0)
+				goto finished;
+			put_fs_byte('\0', buf++), addr++, count--;
+		}
+		n = tmp->size - PAGE_SIZE;
+		if (addr > vaddr)
+			n -= addr - vaddr;
+		while (--n >= 0) {
+			if (count == 0)
+				goto finished;
+			put_fs_byte(*addr++, buf++), count--;
+		}
+	}
+finished:
+	return buf - buf_start;
+}
diff --git a/net/Makefile b/net/Makefile
index 6cfd584..6478d1e 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -10,9 +10,10 @@
 # only these two lines should need to be changed to remove inet sockets.
 # (and the inet/tcpip.o in net.o)
 
-DRIVERS     =
 SUBDIRS     := unix inet
 
+SUBOBJS     := $(foreach f,$(SUBDIRS),$f/$f.o)
+
 .c.o:
 	$(CC) $(CFLAGS) -c $<
 .s.o:
@@ -22,23 +23,22 @@
 
 OBJS	=  Space.o ddi.o socket.o
 
-all:		net.o
+all:		subdirs net.o
 
-net.o:		$(OBJS) subdirs
+net.o:		$(OBJS) network.a
 		$(LD) -r -o net.o $(OBJS) network.a
 
-subdirs:	dummy
-		@rm -f network.a
-		@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) && ar rcs ../network.a $$i.o) || exit; done
-		@ranlib network.a
+network.a:	$(SUBOBJS)
+		rm -f $@
+		ar rc $@ $(SUBOBJS)
+		ranlib $@
 
-clean:
-		rm -f core *.o *.a *.s
-		@for i in $(DRIVERS) $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) clean) || exit; done
+subdirs:	dummy
+		set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
 
 dep:
 		$(CPP) -M *.c > .depend
-		@for i in $(DRIVERS) $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+		set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
 
 dummy:
 
diff --git a/net/inet/Makefile b/net/inet/Makefile
index b4245cf..5d4a675 100644
--- a/net/inet/Makefile
+++ b/net/inet/Makefile
@@ -31,13 +31,6 @@
 
 endif
 
-subdirs:	dummy
-		for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
-
-
-clean:
-		rm -f core *.o *.a *.s
-
 dep:
 		$(CPP) -M *.c > .depend
 
diff --git a/net/inet/README b/net/inet/README
index c98d222..79f957f 100644
--- a/net/inet/README
+++ b/net/inet/README
@@ -1,5 +1,5 @@
 
-NET2Debugged 1.22 README
+NET2Debugged 1.24 README
 ------------------------
 
 Major Changes
@@ -19,8 +19,24 @@
 	Lan workplace for DOS).
 o	Numerous fixes for solidity
 o	Verify_area used properly.
+o	MSG_PEEK is faster again
+o	Minor TCP fixes. Hopefully no more TCP lockups (ha!)
+o	Donald's promiscuous mode. Go forth and write protocol analysers...
+-------------------------------------------------------------------------
+NOTE:
+	Drivers for this stack set must be using alloc_skb() not just
+kmalloc. If you get millions of 'non sk_buff...' errors please check the
+driver you are using. All Donald's drivers know about this. If you have
+a problem driver replace all cases of
+	
+	.. =(struct sk_buff *)kmalloc(sizeof(struct sk_buff)+...
 
+With
+	..=alloc_skb(sizeof(struct sk_buff)+...
 
+And if it uses kfree_s on the packet change that to use kfree_skbmem().
+
+-------------------------------------------------------------------------
 Bug fixes and improvements for this section of the code should be mailed to
 iiitac@pyr.swan.ac.uk.
 
diff --git a/net/inet/arp.c b/net/inet/arp.c
index a3e5ff8..4924988 100644
--- a/net/inet/arp.c
+++ b/net/inet/arp.c
@@ -32,6 +32,9 @@
  *		Alan Cox	:	arp_send memory leak removed
  *		Alan Cox	:	generic skbuff code fixes.
  *		Alan Cox	:	'Bad Packet' only reported on debugging
+ *		Alan Cox	:	Proxy arp.
+ *		Alan Cox	:	skb->link3 maintained by letting the other xmit queue kill the packet.
+ *
  * To Fix:
  *				:	arp response allocates an skbuff to send. However there is a perfectly
  *					good spare skbuff the right size about to be freed (the query). Use the
@@ -109,8 +112,14 @@
   NULL,
 };
 
+static int arp_proxies=0;	/* So we can avoid the proxy arp 
+				   overhead with the usual case of
+				   no proxy arps */
+
 struct sk_buff * volatile arp_q = NULL;
 
+static struct arp_table *arp_lookup(unsigned long addr);
+static struct arp_table *arp_lookup_proxy(unsigned long addr);
 
 /* Dump the ADDRESS bytes of an unknown hardware type. */
 static char *
@@ -214,8 +223,10 @@
 		 * this packet from the queue.  (grinnik) -FvK
 		 */
 		skb->sk = NULL;
-		kfree_skb(skb, FREE_WRITE);
-
+		if(skb->free)
+			kfree_skb(skb, FREE_WRITE);
+			/* If free was 0, magic is now 0, next is 0 and 
+			   the write queue will notice and kill */
 		sti();
 		continue;
 	}
@@ -236,13 +247,26 @@
 
 /* Create and send our response to an ARP request. */
 static int
-arp_response(struct arphdr *arp1, struct device *dev)
+arp_response(struct arphdr *arp1, struct device *dev,  int addrtype)
 {
   struct arphdr *arp2;
   struct sk_buff *skb;
   unsigned long src, dst;
   unsigned char *ptr1, *ptr2;
   int hlen;
+  struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */
+
+  /* Decode the source (REQUEST) message. */
+  ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
+  src = *((unsigned long *) (ptr1 + arp1->ar_hln));
+  dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
+  
+  if(addrtype!=IS_MYADDR)
+  {
+  	apt=arp_lookup_proxy(dst);
+  	if(apt==NULL)
+  		return(1);
+  }
 
   /* Get some mem and initialize it for the return trip. */
   skb = alloc_skb(sizeof(struct sk_buff) +
@@ -254,11 +278,6 @@
 	return(1);
   }
 
-  /* Decode the source (REQUEST) message. */
-  ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
-  src = *((unsigned long *) (ptr1 + arp1->ar_hln));
-  dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
-
   skb->mem_addr = skb;
   skb->len      = sizeof(struct arphdr) + (2 * arp1->ar_hln) + 
 		  (2 * arp1->ar_pln) + dev->hard_header_len;
@@ -283,7 +302,10 @@
   arp2->ar_hln = arp1->ar_hln;
   arp2->ar_pln = arp1->ar_pln;
   arp2->ar_op = htons(ARPOP_REPLY);
-  memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
+  if(addrtype==IS_MYADDR)
+	  memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
+  else		/* Proxy arp, so pull from the table */
+  	  memcpy(ptr2, apt->ha, arp2->ar_hln);
   ptr2 += arp2->ar_hln;
   memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln);
   ptr2 += arp2->ar_pln;
@@ -336,6 +358,30 @@
 }
 
 
+/* This will find a proxy in the ARP table by looking at the IP address. */
+static struct arp_table *arp_lookup_proxy(unsigned long paddr)
+{
+  struct arp_table *apt;
+  unsigned long hash;
+
+  DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr)));
+
+  /* Loop through the table for the desired address. */
+  hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
+  cli();
+  apt = arp_tables[hash];
+  while(apt != NULL) {
+	if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) {
+		sti();
+		return(apt);
+	}
+	apt = apt->next;
+  }
+  sti();
+  return(NULL);
+}
+
+
 /* Delete an ARP mapping entry in the cache. */
 void
 arp_destroy(unsigned long paddr)
@@ -359,6 +405,8 @@
   while ((apt = *lapt) != NULL) {
 	if (apt->ip == paddr) {
 		*lapt = apt->next;
+		if(apt->flags&ATF_PUBL)
+			arp_proxies--;			
 		kfree_s(apt, sizeof(struct arp_table));
 		sti();
 		return;
@@ -391,7 +439,7 @@
   apt->ip = paddr;
   apt->hlen = hlen;
   apt->htype = htype;
-  apt->flags = (ATF_INUSE | ATF_COM);	/* USED and COMPLETED entry */
+  apt->flags = (ATF_INUSE | ATF_COM);		/* USED and COMPLETED entry */
   memcpy(apt->ha, addr, hlen);
   apt->last_used = jiffies;
   cli();
@@ -419,6 +467,7 @@
   unsigned long src, dst;
   unsigned char *ptr;
   int ret;
+  int addr_hint;
 
   DPRINTF((DBG_ARP, "<<\n"));
   arp = skb->h.arp;
@@ -496,7 +545,7 @@
   }
 
   memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
-  if (chk_addr(dst) != IS_MYADDR) {
+  if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) {
 	DPRINTF((DBG_ARP, "ARP: request was not for me!\n"));
 	kfree_skb(skb, FREE_READ);
 	return(0);
@@ -506,7 +555,7 @@
    * Yes, it is for us.
    * Allocate, fill in and send an ARP REPLY packet.
    */
-  ret = arp_response(arp, dev);
+  ret = arp_response(arp, dev, addr_hint);
   kfree_skb(skb, FREE_READ);
   return(ret);
 }
@@ -679,20 +728,7 @@
 	printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic);
 	return;
   }
-#ifdef OLDWAY
-  if (arp_q == NULL) {
-	arp_q = skb;
-	skb->next = skb;
-	skb->prev = skb;
-  } else {
-	skb->next = arp_q;
-	skb->prev = arp_q->prev;
-	skb->next->prev = skb;
-	skb->prev->next = skb;
-  }
-#else
   skb_queue_tail(&arp_q,skb);
-#endif
   skb->magic = ARP_QUEUE_MAGIC;
   sti();
 }
@@ -793,6 +829,8 @@
   memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen);
   apt->last_used = jiffies;
   apt->flags = r.arp_flags;
+  if(apt->flags&ATF_PUBL)
+  	arp_proxies++;		/* Count proxy arps so we know if to use it */
 
   return(0);
 }
diff --git a/net/inet/datagram.c b/net/inet/datagram.c
index cf610ea..d1991f4 100644
--- a/net/inet/datagram.c
+++ b/net/inet/datagram.c
@@ -10,6 +10,7 @@
  *
  *	Fixes:
  *		Alan Cox	:	NULL return from skb_peek_copy() understood
+ *		Alan Cox	:	Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
  */
 
 #include <linux/config.h>
@@ -47,7 +48,8 @@
 	struct sk_buff *skb;
 	
 	/* Socket is inuse - so the timer doesn't attack it */
-  	sk->inuse = 1;
+restart:
+	sk->inuse = 1;
   	while(sk->rqueue == NULL) 	/* No data */
   	{
   		/* If we are shutdown then no more data is going to appear. We are done */
@@ -96,17 +98,50 @@
 	  /* Again only user level code calls this function, so nothing interrupt level
 	     will suddenely eat the rqueue */
 	  if (!(flags & MSG_PEEK)) 
+	  {
 	  	skb=skb_dequeue(&sk->rqueue);
+	  	if(skb!=NULL)
+		  	skb->users++;
+		else
+			goto restart;	/* Avoid race if someone beats us to the data */
+	  }
 	  else
 	  {
-	  	skb=skb_peek_copy(&sk->rqueue);	/* We make a copy with interrupts off. Its the only
-	  					   way to be safe as this code is re-entrant */
+	  	cli();
+	  	skb=skb_peek(&sk->rqueue);
+	  	if(skb!=NULL)
+	  		skb->users++;
+	  	sti();
 	  	if(skb==NULL)	/* shouldn't happen but .. */
-	  		*err=-ENOMEM;
+	  		*err=-EAGAIN;
 	  }
 	  return skb;
 }	
 
+void skb_free_datagram(struct sk_buff *skb)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	skb->users--;
+	if(skb->users>0)
+	{
+		restore_flags(flags);
+		return;
+	}
+	/* See if it needs destroying */
+	if(skb->list == NULL)	/* Been dequeued by someone - ie its read */
+		kfree_skb(skb,FREE_READ);
+	restore_flags(flags);
+}
+
+void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
+{
+	/* We will know all about the fraglist options to allow >4K receives 
+	   but not this release */
+	memcpy_tofs(to,skb->h.raw+offset,size);
+}
 
 /*
  *	Datagram select: Again totally generic. Moved from udp.c
diff --git a/net/inet/dev.c b/net/inet/dev.c
index 65f4ecd..d11d60c 100644
--- a/net/inet/dev.c
+++ b/net/inet/dev.c
@@ -20,6 +20,7 @@
  *		C.E.Hawkins:	IFF_PROMISC support
  *		Alan Cox:	Supports Donald Beckers new hardware 
  *				multicast layer, but not yet multicast lists.
+ *		Alan Cox:	ip_addr_match problems with class A/B nets.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -111,19 +112,19 @@
 ip_addr_match(unsigned long me, unsigned long him)
 {
   int i;
-
+  unsigned long mask=0xFFFFFFFF;
   DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
   DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
 
   if (me == him) 
   	return(1);
-  for (i = 0; i < 4; i++, me >>= 8, him >>= 8) {
+  for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) {
 	if ((me & 0xFF) != (him & 0xFF)) {
 		/*
 		 * The only way this could be a match is for
 		 * the rest of addr1 to be 0 or 255.
 		 */
-		if (me != 0 && me != 255) return(0);
+		if (me != 0 && me != mask) return(0);
 		return(1);
 	}
   }
@@ -190,7 +191,7 @@
 
   DPRINTF((DBG_DEV, "NONE\n"));
   
-  if(addr & 0xFF)
+  if ((addr & 0xFF) == 0xFF)
   {
   	/* Wrong subnetted IS_BROADCAST */
   	return(IS_INVBCAST);
@@ -531,6 +532,8 @@
   /* Any data left to process? */
   while((skb=skb_dequeue(&backlog))!=NULL)
   {
+	flag=0;
+	sti();
        /*
 	* Bump the pointer to the next structure.
 	* This assumes that the basic 'skb' pointer points to
@@ -606,6 +609,7 @@
   }
   in_bh = 0;
   sti();
+  dev_transmit();
 }
 
 
@@ -901,12 +905,12 @@
 	  if ((dev->flags & IFF_UP) == 0
 	      && (retval = dev_open(dev)) != 0)
 	      return retval;
-	  printk("%s: adding HOST route of %8.8x.\n", dev->name,
+	  printk("%s: adding HOST route of %8.8lx.\n", dev->name,
 		 htonl(ipc.paddr));
 	  rt_add(RTF_HOST, ipc.paddr, 0, dev);
 	  if (ipc.router != 0 && ipc.router != -1) {
 	      rt_add(RTF_GATEWAY, ipc.paddr, ipc.router, dev);
-	      printk("%s: adding GATEWAY route of %8.8x.\n",
+	      printk("%s: adding GATEWAY route of %8.8lx.\n",
 		     dev->name, htonl(ipc.paddr));
 
 	  }
diff --git a/net/inet/inet.h b/net/inet/inet.h
index d617de3..455ab7d 100644
--- a/net/inet/inet.h
+++ b/net/inet/inet.h
@@ -52,9 +52,6 @@
 #include <linux/ddi.h>
 
 
-#define AF_INET_MAJOR	18		/* UNIX VFS major number	*/
-
-
 #define NET16(x)	((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
 
 
diff --git a/net/inet/ip.c b/net/inet/ip.c
index 4dff2e2..a1df87b 100644
--- a/net/inet/ip.c
+++ b/net/inet/ip.c
@@ -25,6 +25,10 @@
  *					it copies the frame but at least it 
  *					doesn't eat memory too.
  *		Alan Cox	:	Generic queue code and memory fixes.
+ *		Fred Van Kempen :	IP fragment support (borrowed from NET2E)
+ *		Gerhard Koerting:	Forward fragmented frames correctly.
+ *		Gerhard Koerting: 	Fixes to my fix of the above 8-).
+ *		Gerhard Koerting:	IP interface addressing fix.
  *
  * To Fix:
  *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
@@ -57,7 +61,8 @@
 #include "arp.h"
 #include "icmp.h"
 
-#undef CONFIG_IP_FORWARD
+#define CONFIG_IP_FORWARD
+#define CONFIG_IP_DEFRAG
 
 extern int last_retran;
 extern void sort_send(struct sock *sk);
@@ -217,7 +222,7 @@
 		return(-ENETUNREACH);
 
 	*dev = rt->rt_dev;
-	if (daddr != 0x0100007FL) 
+	if (saddr == 0x0100007FL && daddr != 0x0100007FL) 
 		saddr = rt->rt_dev->pa_addr;
 	raddr = rt->rt_gateway;
 
@@ -486,7 +491,7 @@
 int
 ip_csum(struct iphdr *iph)
 {
-  return (ip_fast_csum((unsigned char *)iph, iph->ihl) != 0);
+  return ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
 /* Generate a checksym for an outgoing IP datagram. */
@@ -497,11 +502,557 @@
    iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
+/************************ Fragment Handlers From NET2E not yet with tweaks to beat 4K **********************************/
+
+static struct ipq *ipqueue = NULL;		/* IP fragment queue	*/
+ /* Create a new fragment entry. */
+static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
+{
+   	struct ipfrag *fp;
+ 
+   	fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+   	if (fp == NULL) 
+   	{
+	 	printk("IP: frag_create: no memory left !\n");
+	 	return(NULL);
+   	}
+  	memset(fp, 0, sizeof(struct ipfrag));
+
+        /* Fill in the structure. */
+	fp->offset = offset;
+	fp->end = end;
+	fp->len = end - offset;
+	fp->skb = skb;
+	fp->ptr = ptr;
+ 
+	return(fp);
+}
+ 
+ 
+/*
+ * Find the correct entry in the "incomplete datagrams" queue for
+ * this IP datagram, and return the queue entry address if found.
+ */
+static struct ipq *ip_find(struct iphdr *iph)
+{
+	struct ipq *qp;
+	struct ipq *qplast;
+ 
+	cli();
+	qplast = NULL;
+	for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) 
+	{
+ 		if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
+			iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) 
+		{
+			del_timer(&qp->timer);	/* So it doesnt vanish on us. The timer will be reset anyway */
+ 			sti();
+ 			return(qp);
+ 		}
+   	}
+	sti();
+	return(NULL);
+}
+ 
+ 
+/*
+ * Remove an entry from the "incomplete datagrams" queue, either
+ * because we completed, reassembled and processed it, or because
+ * it timed out.
+ */
+
+static void ip_free(struct ipq *qp)
+{
+	struct ipfrag *fp;
+	struct ipfrag *xp;
+
+	/* Stop the timer for this entry. */
+/*	printk("ip_free\n");*/
+	del_timer(&qp->timer);
+
+	/* Remove this entry from the "incomplete datagrams" queue. */
+	cli();
+	if (qp->prev == NULL) 
+	{
+	 	ipqueue = qp->next;
+	 	if (ipqueue != NULL) 
+	 		ipqueue->prev = NULL;
+   	} 
+   	else 
+   	{
+ 		qp->prev->next = qp->next;
+ 		if (qp->next != NULL) 
+ 			qp->next->prev = qp->prev;
+   	}
+ 
+   	/* Release all fragment data. */
+/*   	printk("ip_free: kill frag data\n");*/
+   	fp = qp->fragments;
+   	while (fp != NULL) 
+   	{
+ 		xp = fp->next;
+ 		IS_SKB(fp->skb);
+ 		kfree_skb(fp->skb,FREE_READ);
+ 		kfree_s(fp, sizeof(struct ipfrag));
+ 		fp = xp;
+   	}
+   	
+/*   	printk("ip_free: cleanup\n");*/
+ 
+   	/* Release the MAC header. */
+   	kfree_s(qp->mac, qp->maclen);
+ 
+   	/* Release the IP header. */
+   	kfree_s(qp->iph, qp->ihlen + 8);
+ 
+   	/* Finally, release the queue descriptor itself. */
+   	kfree_s(qp, sizeof(struct ipq));
+/*   	printk("ip_free:done\n");*/
+   	sti();
+ }
+ 
+ 
+ /* Oops- a fragment queue timed out.  Kill it and send an ICMP reply. */
+ 
+static void ip_expire(unsigned long arg)
+{
+   	struct ipq *qp;
+ 
+   	qp = (struct ipq *)arg;
+   	DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp));
+ 
+   	/* Send an ICMP "Fragment Reassembly Timeout" message. */
+#if 0   	
+   	icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED,
+ 		    ICMP_EXC_FRAGTIME, qp->iph);
+#endif 		 
+ 	if(qp->fragments!=NULL)
+ 		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
+ 				ICMP_EXC_FRAGTIME, qp->dev);
+ 
+   	/* Nuke the fragment queue. */
+	ip_free(qp);
+}
+ 
+ 
+/*
+ * Add an entry to the 'ipq' queue for a newly received IP datagram.
+ * We will (hopefully :-) receive all other fragments of this datagram
+ * in time, so we just create a queue for this datagram, in which we
+ * will insert the received fragments at their respective positions.
+ */
+
+static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
+{
+  	struct ipq *qp;
+  	int maclen;
+  	int ihlen;
+
+  	qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+  	if (qp == NULL) 
+  	{
+		printk("IP: create: no memory left !\n");
+		return(NULL);
+  	}
+ 	memset(qp, 0, sizeof(struct ipq));
+
+  	/* Allocate memory for the MAC header. */
+  	maclen = ((unsigned long) iph) - ((unsigned long) (skb + 1));
+  	qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
+  	if (qp->mac == NULL) 
+  	{
+		printk("IP: create: no memory left !\n");
+		kfree_s(qp, sizeof(struct ipq));
+		return(NULL);
+  	}
+
+  	/* Allocate memory for the IP header (plus 8 octects for ICMP). */
+  	ihlen = (iph->ihl * sizeof(unsigned long));
+  	qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
+  	if (qp->iph == NULL) 
+  	{
+		printk("IP: create: no memory left !\n");
+		kfree_s(qp->mac, maclen);
+		kfree_s(qp, sizeof(struct ipq));
+		return(NULL);
+  	}
+
+  	/* Fill in the structure. */
+  	memcpy(qp->mac, (skb + 1), maclen);
+ 	memcpy(qp->iph, iph, ihlen + 8);
+  	qp->len = 0;
+  	qp->ihlen = ihlen;
+  	qp->maclen = maclen;
+  	qp->fragments = NULL;
+  	qp->dev = dev;
+/*  	printk("Protocol = %d\n",qp->iph->protocol);*/
+	
+  	/* Start a timer for this entry. */
+  	qp->timer.expires = IP_FRAG_TIME;		/* about 30 seconds	*/
+  	qp->timer.data = (unsigned long) qp;		/* pointer to queue	*/
+  	qp->timer.function = ip_expire;			/* expire function	*/
+  	add_timer(&qp->timer);
+
+  	/* Add this entry to the queue. */
+  	qp->prev = NULL;
+  	cli();
+  	qp->next = ipqueue;
+  	if (qp->next != NULL) 
+  		qp->next->prev = qp;
+  	ipqueue = qp;
+  	sti();
+  	return(qp);
+}
+ 
+ 
+ /* See if a fragment queue is complete. */
+static int ip_done(struct ipq *qp)
+{
+	struct ipfrag *fp;
+	int offset;
+ 
+   	/* Only possible if we received the final fragment. */
+   	if (qp->len == 0) 
+   		return(0);
+ 
+   	/* Check all fragment offsets to see if they connect. */
+  	fp = qp->fragments;
+   	offset = 0;
+   	while (fp != NULL) 
+   	{
+ 		if (fp->offset > offset) 
+ 			return(0);	/* fragment(s) missing */
+ 		offset = fp->end;
+ 		fp = fp->next;
+   	}
+ 
+   	/* All fragments are present. */
+   	return(1);
+ }
+ 
+ 
+/* Build a new IP datagram from all its fragments. */
+static struct sk_buff *ip_glue(struct ipq *qp)
+{
+	struct sk_buff *skb;
+   	struct iphdr *iph;
+   	struct ipfrag *fp;
+   	unsigned char *ptr;
+   	int count, len;
+ 
+   	/* Allocate a new buffer for the datagram. */
+   	len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len;
+   	if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) 
+   	{
+ 		printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
+ 		ip_free(qp);
+ 		return(NULL);
+   	}
+ 
+   	/* Fill in the basic details. */
+   	skb->len = (len - qp->maclen);
+   	skb->h.raw = (unsigned char *) (skb + 1);
+   	skb->free = 1;
+   	skb->lock = 1;
+ 
+   	/* Copy the original MAC and IP headers into the new buffer. */
+   	ptr = (unsigned char *) skb->h.raw;
+   	memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
+/*   	printk("Copied %d bytes of mac header.\n",qp->maclen);*/
+   	ptr += qp->maclen;
+   	memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+/*   	printk("Copied %d byte of ip header.\n",qp->ihlen);*/
+   	ptr += qp->ihlen;
+   	skb->h.raw += qp->maclen;
+   	
+/*   	printk("Protocol = %d\n",skb->h.iph->protocol);*/
+   	count = 0;
+ 
+   	/* Copy the data portions of all fragments into the new buffer. */
+   	fp = qp->fragments;
+   	while(fp != NULL) 
+   	{
+   		if(count+fp->len>skb->len)
+   		{
+   			printk("Invalid fragment list: Fragment over size.\n");
+   			kfree_skb(skb,FREE_WRITE);
+   			return NULL;
+   		}
+/*   		printk("Fragment %d size %d\n",fp->offset,fp->len);*/
+ 		memcpy((ptr + fp->offset), fp->ptr, fp->len);
+ 		count += fp->len;
+ 		fp = fp->next;
+   	}
+ 
+   	/* We glued together all fragments, so remove the queue entry. */
+   	ip_free(qp);
+ 
+   	/* Done with all fragments. Fixup the new IP header. */
+   	iph = skb->h.iph;
+   	iph->frag_off = 0;
+   	iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
+   	return(skb);
+}
+ 
+
+/* Process an incoming IP datagram fragment. */
+static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
+{
+	struct ipfrag *prev, *next;
+	struct ipfrag *tfp;
+	struct ipq *qp;
+	struct sk_buff *skb2;
+	unsigned char *ptr;
+	int flags, offset;
+	int i, ihl, end;
+
+	/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
+   	qp = ip_find(iph);
+ 
+   	/* Is this a non-fragmented datagram? */
+   	offset = ntohs(iph->frag_off);
+   	flags = offset & ~IP_OFFSET;
+   	offset &= IP_OFFSET;
+   	if (((flags & IP_MF) == 0) && (offset == 0)) 
+   	{
+ 		if (qp != NULL) 
+ 			ip_free(qp);	/* Huh? How could this exist?? */
+ 		return(skb);
+   	}
+   	offset <<= 3;		/* offset is in 8-byte chunks */
+ 
+   	/*
+    	 * If the queue already existed, keep restarting its timer as long
+   	 * as we still are receiving fragments.  Otherwise, create a fresh
+    	 * queue entry.
+    	 */
+   	if (qp != NULL) 
+   	{
+ 		del_timer(&qp->timer);
+ 		qp->timer.expires = IP_FRAG_TIME;	/* about 30 seconds	*/
+ 		qp->timer.data = (unsigned long) qp;	/* pointer to queue	*/
+ 		qp->timer.function = ip_expire;		/* expire function	*/
+ 		add_timer(&qp->timer);
+   	} 
+   	else 
+   	{
+ 		if ((qp = ip_create(skb, iph, dev)) == NULL) 
+ 			return(NULL);
+   	}
+ 
+   	/* Determine the position of this fragment. */
+   	ihl = (iph->ihl * sizeof(unsigned long));
+   	end = offset + ntohs(iph->tot_len) - ihl;
+ 
+   	/* Point into the IP datagram 'data' part. */
+   	ptr = ((unsigned char *) (skb + 1)) + dev->hard_header_len + ihl;
+ 
+   	/* Is this the final fragment? */
+   	if ((flags & IP_MF) == 0) 
+   		qp->len = end;
+ 
+   	/*
+   	 * Find out which fragments are in front and at the back of us
+   	 * in the chain of fragments so far.  We must know where to put
+   	 * this fragment, right?
+   	 */
+   	prev = NULL;
+   	for(next = qp->fragments; next != NULL; next = next->next) 
+   	{
+ 		if (next->offset > offset) 
+ 			break;	/* bingo! */
+ 		prev = next;
+   	}	
+ 
+   	/*
+   	 * We found where to put this one.
+   	 * Check for overlap with preceeding fragment, and, if needed,
+   	 * align things so that any overlaps are eliminated.
+   	 */
+   	if (prev != NULL && offset < prev->end) 
+   	{
+ 		i = prev->end - offset;
+ 		offset += i;	/* ptr into datagram */
+ 		ptr += i;	/* ptr into fragment data */
+ 		DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i));
+   	}	
+ 
+   	/*
+    	 * Look for overlap with succeeding segments.
+    	 * If we can merge fragments, do it.
+      	 */
+   
+   	for(; next != NULL; next = tfp) 
+   	{
+ 		tfp = next->next;
+ 		if (next->offset >= end) 
+ 			break;		/* no overlaps at all */
+ 
+ 		i = end - next->offset;			/* overlap is 'i' bytes */
+ 		next->len -= i;				/* so reduce size of	*/
+ 		next->offset += i;			/* next fragment	*/
+ 		next->ptr += i;
+ 		
+ 		/* If we get a frag size of <= 0, remove it. */
+ 		if (next->len <= 0) 
+ 		{
+ 			DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n",
+ 							next, next->len));
+ 			if (next->prev != NULL) 
+ 				next->prev->next = next->next;
+ 		  	else 
+ 		  		qp->fragments = next->next;
+ 		
+ 			if (tfp->next != NULL) 
+ 				next->next->prev = next->prev;
+ 			
+ 			kfree_s(next, sizeof(struct ipfrag));
+ 		}
+ 		DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i));
+   	}
+ 
+   	/* Insert this fragment in the chain of fragments. */
+   	tfp = NULL;
+   	tfp = ip_frag_create(offset, end, skb, ptr);
+   	tfp->prev = prev;
+   	tfp->next = next;
+   	if (prev != NULL) 
+   		prev->next = tfp;
+     	else 
+     		qp->fragments = tfp;
+   
+   	if (next != NULL) 
+   		next->prev = tfp;
+ 
+   	/*
+    	 * OK, so we inserted this new fragment into the chain.
+    	 * Check if we now have a full IP datagram which we can
+    	 * bump up to the IP layer...
+    	 */
+   
+   	if (ip_done(qp)) 
+   	{
+ 		skb2 = ip_glue(qp);		/* glue together the fragments */
+ 		return(skb2);
+   	}
+   	return(NULL);
+ }
+ 
+ 
+ /*
+  * This IP datagram is too large to be sent in one piece.  Break it up into
+  * smaller pieces (each of size equal to the MAC header plus IP header plus
+  * a block of the data of the original IP data part) that will yet fit in a
+  * single device frame, and queue such a frame for sending by calling the
+  * ip_queue_xmit().  Note that this is recursion, and bad things will happen
+  * if this function causes a loop...
+  */
+ void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
+ {
+   	struct iphdr *iph;
+   	unsigned char *raw;
+   	unsigned char *ptr;
+   	struct sk_buff *skb2;
+   	int left, mtu, hlen, len;
+   	int offset;
+ 
+   	/* Point into the IP datagram header. */
+   	raw = (unsigned char *) (skb + 1);
+   	iph = (struct iphdr *) (raw + dev->hard_header_len);
+ 	
+   	/* Setup starting values. */
+   	hlen = (iph->ihl * sizeof(unsigned long));
+   	left = ntohs(iph->tot_len) - hlen;
+   	hlen += dev->hard_header_len;
+   	mtu = (dev->mtu - hlen);
+   	ptr = (raw + hlen);
+ 	
+   	DPRINTF((DBG_IP, "IP: Fragmentation Desired\n"));
+   	DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
+ 		dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
+   	DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
+ 
+   	/* Check for any "DF" flag. */
+   	if (ntohs(iph->frag_off) & IP_DF) 
+   	{
+ 		DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n"));
+ 		DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
+ 			dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
+ 		DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
+ 
+ 		/*
+ 		 * FIXME:
+ 		 * We should send an ICMP warning message here!
+ 		 */
+ 		 
+ 		icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); 
+ 		return;
+   	}
+ 
+   	/* Fragment the datagram. */
+	if (is_frag & 2)
+	  offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
+	else
+   	  offset = 0;
+   	while(left > 0) 
+   	{
+ 		len = left;
+ 		if (len+8 > mtu) 
+ 			len = (dev->mtu - hlen - 8);
+ 		if ((left - len) >= 8) 
+ 		{
+ 			len /= 8;
+ 			len *= 8;
+ 		}
+ 		DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n",
+ 							len, len + hlen));
+ 
+ 		/* Allocate buffer. */
+ 		if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_KERNEL)) == NULL) 
+ 		{
+ 			printk("IP: frag: no memory for new fragment!\n");
+ 			return;
+ 		}
+ 		skb2->arp = skb->arp;
+ 		skb2->free = skb->free;
+ 		skb2->len = len + hlen;
+ 		skb2->h.raw=(char *)(skb2+1);
+ 
+ 		if (sk) 
+ 			sk->wmem_alloc += skb2->mem_len;
+ 
+ 		/* Copy the packet header into the new buffer. */
+ 		memcpy(skb2->h.raw, raw, hlen);
+ 
+ 		/* Copy a block of the IP datagram. */
+ 		memcpy(skb2->h.raw + hlen, ptr, len);
+ 		left -= len;
+
+		skb2->h.raw+=dev->hard_header_len; 
+ 		/* Fill in the new header fields. */
+ 		iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
+ 		iph->frag_off = htons((offset >> 3));
+ 		/* Added AC : If we are fragmenting a fragment thats not the
+ 		   last fragment then keep MF on each bit */
+ 		if (left > 0 || (is_frag & 1)) 
+ 			iph->frag_off |= htons(IP_MF);
+ 		ptr += len;
+ 		offset += len;
+/* 		printk("Queue frag\n");*/
+ 
+ 		/* Put this fragment into the sending queue. */
+ 		ip_queue_xmit(sk, dev, skb2, 1);
+/* 		printk("Queued\n");*/
+   	}
+ }
+ 
+
+
 #ifdef CONFIG_IP_FORWARD
 
 /* Forward an IP datagram to its next destination. */
 static void
-ip_forward(struct sk_buff *skb, struct device *dev)
+ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
 {
   struct device *dev2;
   struct iphdr *iph;
@@ -511,6 +1062,17 @@
   unsigned long raddr;
 
   /*
+   * Only forward packets that were fired at us when we are in promiscuous
+   * mode. In standard mode we rely on the driver to filter for us.
+   */
+   
+  if(dev->flags&IFF_PROMISC)
+  {
+  	if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
+  		return;
+  }
+  
+  /*
    * According to the RFC, we must first decrease the TTL field. If
    * that reaches zero, we must reply an ICMP control message telling
    * that the packet's lifetime expired.
@@ -589,13 +1151,18 @@
 	skb2->h.raw = ptr;
 
 	/* Copy the packet data into the new buffer. */
-	skb2->h.raw = ptr;
 	memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
 		
 	/* Now build the MAC header. */
 	(void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
 
-	dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL);
+	if(skb2->len > dev2->mtu)
+	{
+		ip_fragment(NULL,skb2,dev2, is_frag);
+		kfree_skb(skb2,FREE_WRITE);
+	}
+	else
+		dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL);
   }
 }
 
@@ -614,6 +1181,7 @@
   static struct options opt; /* since we don't use these yet, and they
 				take up stack space. */
   int brd;
+  int is_frag=0;
 
   DPRINTF((DBG_IP, "<<\n"));
 
@@ -626,7 +1194,7 @@
 	kfree_skb(skb, FREE_WRITE);
 	return(0);
   }
-
+  
   if (iph->ihl != 5) {  	/* Fast path for the typical optionless IP packet. */
       ip_print(iph);		/* Bogus, only for debugging. */
       memset((char *) &opt, 0, sizeof(opt));
@@ -635,34 +1203,37 @@
       opts_p = 1;
   }
 
+  if (iph->frag_off & 0x0020)
+  	is_frag|=1;
+  if (ntohs(iph->frag_off) & 0x1fff)
+  	is_frag|=2;
+  	
   /* Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday. */
   if ((brd = chk_addr(iph->daddr)) == 0) {
 #ifdef CONFIG_IP_FORWARD
-	ip_forward(skb, dev);
-#endif
+	ip_forward(skb, dev, is_frag);
+#else
 	printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
 			iph->saddr,iph->daddr);
+#endif			
 	skb->sk = NULL;
 	kfree_skb(skb, FREE_WRITE);
 	return(0);
   }
 
-  if(brd==IS_INVBCAST)
-  {
-/*	printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n",
-		iph->saddr,iph->daddr);*/
-  	skb->sk=NULL;
-  	kfree_skb(skb,FREE_WRITE);
-  	return(0);
-  }
-  
   /*
    * Reassemble IP fragments. 
    */
 
-  if ((iph->frag_off & 0x0020) || (ntohs(iph->frag_off) & 0x1fff)) {
+  if(is_frag)
+  {
 #ifdef CONFIG_IP_DEFRAG
-      ip_defrag(skb);
+        skb=ip_defrag(iph,skb,dev);
+        if(skb==NULL)
+        {
+        	return 0;
+        }
+        iph=skb->h.iph;
 #else
 	printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
 	printk("    SRC = %s   ", in_ntoa(iph->saddr));
@@ -674,6 +1245,17 @@
 #endif
   }
 
+
+
+  if(brd==IS_INVBCAST)
+  {
+/*	printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n",
+		iph->saddr,iph->daddr);*/
+  	skb->sk=NULL;
+  	kfree_skb(skb,FREE_WRITE);
+  	return(0);
+  }
+  
   /* Point into the IP datagram, just past the header. */
   skb->h.raw += iph->ihl*4;
   hash = iph->protocol & (MAX_INET_PROTOS -1);
@@ -764,7 +1346,17 @@
   ptr = (unsigned char *)(skb + 1);
   ptr += dev->hard_header_len;
   iph = (struct iphdr *)ptr;
-  iph->tot_len = ntohs(skb->len - dev->hard_header_len);
+  iph->tot_len = ntohs(skb->len-dev->hard_header_len);
+
+  if(skb->len > dev->mtu)
+  {
+/*  	printk("Fragment!\n");*/
+  	ip_fragment(sk,skb,dev,0);
+  	IS_SKB(skb);
+  	kfree_skb(skb,FREE_WRITE);
+  	return;
+  }
+  
   ip_send_check(iph);
   ip_print(iph);
   skb->next = NULL;
@@ -838,7 +1430,7 @@
 		   the frame in twice. Because of the technique used this
 		   would be a little sad */
 	if (!skb->arp) {
-		if (dev->rebuild_header((struct enet_header *)(skb+1),dev)) {
+		if (dev->rebuild_header(skb+1, dev)) {
 			sti();	/* Failed to rebuild - next */
 			if (!all) break;
 			skb = (struct sk_buff *)skb->link3;
@@ -901,3 +1493,4 @@
 		}
 	}
 }
+
diff --git a/net/inet/ip.h b/net/inet/ip.h
index 611dd1f..3c01cc8 100644
--- a/net/inet/ip.h
+++ b/net/inet/ip.h
@@ -21,8 +21,44 @@
 
 #include <linux/ip.h>
 
+
 #include "sock.h"	/* struct sock */
 
+/* IP flags. */
+#define IP_CE		0x8000		/* Flag: "Congestion"		*/
+#define IP_DF		0x4000		/* Flag: "Don't Fragment"	*/
+#define IP_MF		0x2000		/* Flag: "More Fragments"	*/
+#define IP_OFFSET	0x1FFF		/* "Fragment Offset" part	*/
+
+#define IP_FRAG_TIME	(30 * HZ)		/* fragment lifetime	*/
+
+
+/* Describe an IP fragment. */
+struct ipfrag {
+  int		offset;		/* offset of fragment in IP datagram	*/
+  int		end;		/* last byte of data in datagram	*/
+  int		len;		/* length of this fragment		*/
+  struct sk_buff *skb;			/* complete received fragment		*/
+  unsigned char		*ptr;		/* pointer into real fragment data	*/
+  struct ipfrag		*next;		/* linked list pointers			*/
+  struct ipfrag		*prev;
+};
+
+/* Describe an entry in the "incomplete datagrams" queue. */
+struct ipq	 {
+  unsigned char		*mac;		/* pointer to MAC header		*/
+  struct iphdr	*iph;		/* pointer to IP header			*/
+  int		len;		/* total length of original datagram	*/
+  short			ihlen;		/* length of the IP header		*/
+  short 	maclen;		/* length of the MAC header		*/
+  struct timer_list timer;	/* when will this queue expire?		*/
+  struct ipfrag		*fragments;	/* linked list of received fragments	*/
+  struct ipq	*next;		/* linked list pointers			*/
+  struct ipq	*prev;
+  struct device *dev;		/* Device - for icmp replies */
+};
+
+
 extern int		backoff(int n);
 
 extern void		ip_print(struct iphdr *ip);
diff --git a/net/inet/packet.c b/net/inet/packet.c
index a9fcdf2..c0585a6 100644
--- a/net/inet/packet.c
+++ b/net/inet/packet.c
@@ -17,6 +17,7 @@
  *		Alan Cox	:	Now uses generic datagram routines I
  *					added. Also fixed the peek/read crash
  *					from all old Linux datagram code.
+ *		Alan Cox	:	Uses the improved datagram code.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -212,7 +213,7 @@
   	return err;
   copied = min(len, skb->len);
 
-  memcpy_tofs(to, skb+1, copied);
+  memcpy_tofs(to, skb+1, copied);	/* Don't use skb_copy_datagram here: We can't get frag chains */
 
   /* Copy the address. */
   if (saddr) {
@@ -224,7 +225,7 @@
 	memcpy_tofs(saddr, &addr, sizeof(*saddr));
   }
 
-  kfree_skb(skb, FREE_READ);	/* Its either been used up, or its a peek_copy anyway */
+  skb_free_datagram(skb);		/* Its either been used up, or its a peek_copy anyway */
 
   release_sock(sk);
   return(copied);
diff --git a/net/inet/proc.c b/net/inet/proc.c
index db4f840..80e80a8 100644
--- a/net/inet/proc.c
+++ b/net/inet/proc.c
@@ -80,11 +80,11 @@
 		timer_active = del_timer(&sp->timer);
 		if (!timer_active)
 			sp->timer.expires = 0;
-		pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %08X %d\n",
+		pos+=sprintf(pos, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
 			i, src, srcp, dest, destp, sp->state, 
 			format==0?sp->send_seq-sp->rcv_ack_seq:sp->rmem_alloc, 
 			format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc,
-			timer_active, sp->timer.expires, sp->retransmits,
+			timer_active, sp->timer.expires, (unsigned) sp->retransmits,
 			SOCK_INODE(sp->socket)->i_uid);
 		if (timer_active)
 			add_timer(&sp->timer);
diff --git a/net/inet/raw.c b/net/inet/raw.c
index c05ccf2..04f3473 100644
--- a/net/inet/raw.c
+++ b/net/inet/raw.c
@@ -17,6 +17,7 @@
  *		Alan Cox	: 	Now uses generic datagrams and shared skbuff
  *					library. No more peek crashes, no more backlogs
  *		Alan Cox	:	Checks sk->broadcast.
+ *		Alan Cox	:	Uses skb_free_datagram/skb_copy_datagram
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -238,7 +239,7 @@
 
   skb->len = tmp + len;
   
-  if(dev!=NULL && skb->len > dev->mtu)
+  if(dev!=NULL && skb->len > 4095)
   {
   	kfree_skb(skb, FREE_WRITE);
   	release_sock(sk);
@@ -337,7 +338,8 @@
   	return err;
 
   copied = min(len, skb->len);
-  memcpy_tofs(to, skb->h.raw,  copied);
+  
+  skb_copy_datagram(skb, 0, to, copied);
 
   /* Copy the address. */
   if (sin) {
@@ -349,7 +351,7 @@
 	memcpy_tofs(sin, &addr, sizeof(*sin));
   }
 
-  kfree_skb(skb, FREE_READ);
+  skb_free_datagram(skb);
   release_sock(sk);
   return (copied);
 }
diff --git a/net/inet/route.c b/net/inet/route.c
index 856a9d4..1bdde0d 100644
--- a/net/inet/route.c
+++ b/net/inet/route.c
@@ -13,6 +13,8 @@
  * Fixes:
  *		Alan Cox	:	Verify area fixes.
  *		Alan Cox	:	cli() protects routing changes
+ *		Rui Oliveira	:	ICMP routing table updates
+ *		(rco@di.uminho.pt)	Routing table insertion and update
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -50,11 +52,11 @@
 {
   if (rt == NULL || inet_debug != DBG_RT) return;
 
-  printk("RT: %06lx NXT=%06lx FLAGS=0x%02lx\n",
+  printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
 		(long) rt, (long) rt->rt_next, rt->rt_flags);
   printk("    TARGET=%s ", in_ntoa(rt->rt_dst));
   printk("GW=%s ", in_ntoa(rt->rt_gateway));
-  printk("    DEV=%s USE=%ld REF=%ld\n",
+  printk("    DEV=%s USE=%ld REF=%d\n",
 	(rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
 	rt->rt_use, rt->rt_refcnt);
 }
@@ -149,8 +151,15 @@
   if (flags & RTF_DYNAMIC) {
 	if (flags & RTF_HOST)
 		rt->rt_dst = dst;
-	else
+	else{
 		rt->rt_dst = (dst & dev->pa_mask);
+		/* We don't want new routes to our own net*/
+		if(rt->rt_dst == (dev->pa_addr & dev->pa_mask)){
+			kfree_s(rt, sizeof(struct rtable));
+			/*printk("Dynamic route to my own net rejected\n");*/
+			return;
+		}
+	}
   } else rt->rt_dst = dst;
 
   rt_print(rt);
@@ -193,7 +202,11 @@
 		restore_flags(cpuflags);
 		return;
 	}
+	r1 = r;
+  }
 
+  r1 = rt_base;
+  for (r = rt_base; r != NULL; r = r->rt_next) {
 	if (! (r->rt_dst & mask)) {
 		DPRINTF((DBG_RT, "RT: adding before r=%X\n", r));
 		rt_print(r);
@@ -290,7 +303,7 @@
   
   /* This isn't quite right -- r->rt_dst is a struct! */
   for (r = rt_base; r != NULL; r = r->rt_next) {
-        pos += sprintf(pos, "%s\t%08X\t%08X\t%02X\t%d\t%d\t%d\n",
+        pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\n",
 		r->rt_dev->name, r->rt_dst, r->rt_gateway,
 		r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric);
   }
diff --git a/net/inet/skbuff.c b/net/inet/skbuff.c
index 144fd55..84f1f7e 100644
--- a/net/inet/skbuff.c
+++ b/net/inet/skbuff.c
@@ -58,19 +58,19 @@
 	{
 		printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
 			file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%ld, list=%p, free=%d\n",
+		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
 			skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
 	}
 	if(skb->magic_debug_cookie!=SK_GOOD_SKB)
 	{
 		printk("File: %s Line %d, passed a non skb!\n", file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%ld, list=%p, free=%d\n",
+		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
 			skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
 	}
 	if(skb->mem_len!=skb->truesize)
 	{
 		printk("File: %s Line %d, Dubious size setting!\n",file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%ld, list=%p\n",
+		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p\n",
 			skb,skb->truesize,skb->mem_len,skb->magic,skb->list);
 	}
 	/* Guess it might be acceptable then */
@@ -80,28 +80,28 @@
  *	Insert an sk_buff at the start of a list.
  */
     
-void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *new)
+void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk)
 {
 	unsigned long flags;
 	
-	IS_SKB(new);	
-	if(new->list)
+	IS_SKB(newsk);	
+	if(newsk->list)
 		printk("Suspicious queue head: sk_buff on list!\n");
 	save_flags(flags);
 	cli();
-	new->list=list;
+	newsk->list=list;
 	
-	new->next=*list;
+	newsk->next=*list;
 	
 	if(*list)
-		new->prev=(*list)->prev;
+		newsk->prev=(*list)->prev;
 	else
-		new->prev=new;
-	new->prev->next=new;
-	new->next->prev=new;
-	IS_SKB(new->prev);
-	IS_SKB(new->next);
-	*list=new;
+		newsk->prev=newsk;
+	newsk->prev->next=newsk;
+	newsk->next->prev=newsk;
+	IS_SKB(newsk->prev);
+	IS_SKB(newsk->next);
+	*list=newsk;
 	restore_flags(flags);
 }
 
@@ -109,33 +109,33 @@
  *	Insert an sk_buff at the end of a list.
  */
  
-void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *new)
+void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
 {
 	unsigned long flags;
 	
-	if(new->list)
+	if(newsk->list)
 		printk("Suspicious queue tail: sk_buff on list!\n");
 	
-	IS_SKB(new);
+	IS_SKB(newsk);
 	save_flags(flags);
 	cli();
 
-	new->list=list;
+	newsk->list=list;
 	if(*list)
 	{
-		(*list)->prev->next=new;
-		(*list)->prev=new;
-		new->next=*list;
-		new->prev=(*list)->prev;
+		(*list)->prev->next=newsk;
+		newsk->prev=(*list)->prev;
+		newsk->next=*list;
+		(*list)->prev=newsk;
 	}
 	else
 	{
-		new->next=new;
-		new->prev=new;
-		*list=new;
+		newsk->next=newsk;
+		newsk->prev=newsk;
+		*list=newsk;
 	}
-	IS_SKB(new->prev);
-	IS_SKB(new->next);		
+	IS_SKB(newsk->prev);
+	IS_SKB(newsk->next);		
 	restore_flags(flags);
 
 }
@@ -185,25 +185,25 @@
  *	Insert a packet before another one in a list.
  */
  
-void skb_insert(struct sk_buff *old, struct sk_buff *new)
+void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
 	IS_SKB(old);
-	IS_SKB(new);
+	IS_SKB(newsk);
 		
 	if(!old->list)
 		printk("insert before unlisted item!\n");
-	if(new->list)
+	if(newsk->list)
 		printk("inserted item is already on a list.\n");
 		
 	save_flags(flags);
 	cli();
-	new->list=old->list;
-	new->next=old;
-	new->prev=old->prev;
-	new->next->prev=new;
-	new->prev->next=new;
+	newsk->list=old->list;
+	newsk->next=old;
+	newsk->prev=old->prev;
+	newsk->next->prev=newsk;
+	newsk->prev->next=newsk;
 	
 	restore_flags(flags);
 }
@@ -212,25 +212,25 @@
  *	Place a packet after a given packet in a list.
  */
  
-void skb_append(struct sk_buff *old, struct sk_buff *new)
+void skb_append(struct sk_buff *old, struct sk_buff *newsk)
 {
 	unsigned long flags;
 	
 	IS_SKB(old);
-	IS_SKB(new);
+	IS_SKB(newsk);
 
 	if(!old->list)
 		printk("append before unlisted item!\n");
-	if(new->list)
+	if(newsk->list)
 		printk("append item is already on a list.\n");
 		
 	save_flags(flags);
 	cli();
-	new->list=old->list;
-	new->prev=old;
-	new->next=old->next;
-	new->next->prev=new;
-	new->prev->next=new;
+	newsk->list=old->list;
+	newsk->prev=old;
+	newsk->next=old->next;
+	newsk->next->prev=newsk;
+	newsk->prev->next=newsk;
 	
 	restore_flags(flags);
 }
@@ -311,7 +311,7 @@
  
 struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
 {
-	struct sk_buff *orig,*new;
+	struct sk_buff *orig,*newsk;
 	unsigned long flags;
 	unsigned int len;
 	/* Now for some games to avoid races */
@@ -330,41 +330,41 @@
 		len=orig->truesize;
 		restore_flags(flags);
 
-		new=alloc_skb(len,GFP_KERNEL);	/* May sleep */
+		newsk=alloc_skb(len,GFP_KERNEL);	/* May sleep */
 
-		if(new==NULL)		/* Oh dear... not to worry */
+		if(newsk==NULL)		/* Oh dear... not to worry */
 			return NULL;
 	
 		save_flags(flags);
 		cli();
 		if(skb_peek(list)!=orig)	/* List changed go around another time */
 		{
-			restore_flags(list);
-			new->sk=NULL;
-			new->free=1;
-			new->mem_addr=new;
-			new->mem_len=len;
-			kfree_skb(new, FREE_WRITE);
+			restore_flags(flags);
+			newsk->sk=NULL;
+			newsk->free=1;
+			newsk->mem_addr=newsk;
+			newsk->mem_len=len;
+			kfree_skb(newsk, FREE_WRITE);
 			continue;
 		}
 		
 		IS_SKB(orig);
-		IS_SKB(new);
-		memcpy(new,orig,len);
-		new->list=NULL;
-		new->magic=0;
-		new->next=NULL;
-		new->prev=NULL;
-		new->mem_addr=new;
-		new->h.raw+=((char *)new-(char *)orig);
-		new->link3=NULL;
-		new->sk=NULL;
-		new->free=1;
+		IS_SKB(newsk);
+		memcpy(newsk,orig,len);
+		newsk->list=NULL;
+		newsk->magic=0;
+		newsk->next=NULL;
+		newsk->prev=NULL;
+		newsk->mem_addr=newsk;
+		newsk->h.raw+=((char *)newsk-(char *)orig);
+		newsk->link3=NULL;
+		newsk->sk=NULL;
+		newsk->free=1;
 	}
 	while(0);
 	
 	restore_flags(flags);
-	return(new);
+	return(newsk);
 }	
 	
 /*
@@ -410,9 +410,11 @@
  	skb->truesize=size;
  	skb->mem_len=size;
  	skb->mem_addr=skb;
+ 	skb->fraglist=NULL;
  	net_memory+=size;
  	net_skbcount++;
  	skb->magic_debug_cookie=SK_GOOD_SKB;
+ 	skb->users=0;
  	return skb;
  }
 
diff --git a/net/inet/skbuff.h b/net/inet/skbuff.h
index 66cb498..6d5f879 100644
--- a/net/inet/skbuff.h
+++ b/net/inet/skbuff.h
@@ -14,6 +14,9 @@
  * Fixes:
  *		Alan Cox		: 	Volatiles (this makes me unhappy - we want proper asm linked list stuff)
  *		Alan Cox		:	Declaration for new primitives
+ *		Alan Cox		:	Fraglist support (idea by Donald Becker)
+ *		Alan Cox		:	'users' counter. Combines with datagram changes to avoid skb_peek_copy
+ *						being used.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -23,6 +26,11 @@
 #ifndef _SKBUFF_H
 #define _SKBUFF_H
 #include <linux/malloc.h>
+
+#ifdef CONFIG_IPX
+#include "ipx.h"
+#endif
+
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
 
 
@@ -48,9 +56,14 @@
 	struct arphdr	*arp;
 	unsigned char	*raw;
 	unsigned long	seq;
+#ifdef CONFIG_IPX	
+	ipx_packet	*ipx;
+#endif	
   } h;
   unsigned long			mem_len;
   unsigned long 		len;
+  unsigned long			fraglen;
+  struct sk_buff		*fraglist;	/* Fragment list */
   unsigned long			truesize;
   unsigned long 		saddr;
   unsigned long 		daddr;
@@ -61,6 +74,7 @@
 				arp,
 				urg_used;
   unsigned char			tries,lock;	/* Lock is now unused */
+  unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
 };
 
 #define SK_WMEM_MAX	8192
@@ -74,8 +88,8 @@
 extern void			skb_queue_head(struct sk_buff * volatile *list,struct sk_buff *buf);
 extern void			skb_queue_tail(struct sk_buff * volatile *list,struct sk_buff *buf);
 extern struct sk_buff *		skb_dequeue(struct sk_buff * volatile *list);
-extern void 			skb_insert(struct sk_buff *old,struct sk_buff *new);
-extern void			skb_append(struct sk_buff *old,struct sk_buff *new);
+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 void 			skb_new_list_head(struct sk_buff *volatile* list);
 extern struct sk_buff *		skb_peek(struct sk_buff * volatile *list);
@@ -88,4 +102,6 @@
 
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
 extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
+extern void			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
+extern void			skb_free_datagram(struct sk_buff *skb);
 #endif	/* _SKBUFF_H */
diff --git a/net/inet/sock.c b/net/inet/sock.c
index 106e763..2d082fa 100644
--- a/net/inet/sock.c
+++ b/net/inet/sock.c
@@ -44,6 +44,8 @@
  *		Rick Sladkey	:	Relaxed UDP rules for matching packets.
  *		C.E.Hawkins	:	IFF_PROMISC/SIOCGHWADDR support
  *	Pauline Middelink	:	Pidentd support
+ *		Alan Cox	:	Fixed connect() taking signals I think.
+ *		Alan Cox	:	SO_LINGER supported
  *
  * To Fix:
  *
@@ -53,17 +55,26 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  */
+
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/string.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
 #include "inet.h"
 #include "dev.h"
 #include "ip.h"
@@ -74,11 +85,6 @@
 #include "udp.h"
 #include "skbuff.h"
 #include "sock.h"
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
 #include "raw.h"
 #include "icmp.h"
 
@@ -98,24 +104,24 @@
 	printk("  print_sk(NULL)\n");
 	return;
   }
-  printk("  wmem_alloc = %d\n", sk->wmem_alloc);
-  printk("  rmem_alloc = %d\n", sk->rmem_alloc);
+  printk("  wmem_alloc = %lu\n", sk->wmem_alloc);
+  printk("  rmem_alloc = %lu\n", sk->rmem_alloc);
   printk("  send_head = %p\n", sk->send_head);
   printk("  state = %d\n",sk->state);
   printk("  wback = %p, rqueue = %p\n", sk->wback, sk->rqueue);
   printk("  wfront = %p\n", sk->wfront);
-  printk("  daddr = %X, saddr = %X\n", sk->daddr,sk->saddr);
+  printk("  daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr);
   printk("  num = %d", sk->num);
   printk(" next = %p\n", sk->next);
-  printk("  send_seq = %d, acked_seq = %d, copied_seq = %d\n",
+  printk("  send_seq = %ld, acked_seq = %ld, copied_seq = %ld\n",
 	  sk->send_seq, sk->acked_seq, sk->copied_seq);
-  printk("  rcv_ack_seq = %d, window_seq = %d, fin_seq = %d\n",
+  printk("  rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n",
 	  sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
   printk("  prot = %p\n", sk->prot);
   printk("  pair = %p, back_log = %p\n", sk->pair,sk->back_log);
   printk("  inuse = %d , blog = %d\n", sk->inuse, sk->blog);
   printk("  dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
-  printk("  retransmits = %d, timeout = %d\n", sk->retransmits, sk->timeout);
+  printk("  retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout);
   printk("  cong_window = %d, packets_out = %d\n", sk->cong_window,
 	  sk->packets_out);
   printk("  urg = %d shutdown=%d\n", sk->urg, sk->shutdown);
@@ -131,7 +137,7 @@
   }
   printk("  prev = %p, next = %p\n", skb->prev, skb->next);
   printk("  sk = %p link3 = %p\n", skb->sk, skb->link3);
-  printk("  mem_addr = %p, mem_len = %d\n", skb->mem_addr, skb->mem_len);
+  printk("  mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len);
   printk("  used = %d free = %d\n", skb->used,skb->free);
 }
 
@@ -471,6 +477,7 @@
   struct sock *sk;
   int val;
   int err;
+  struct linger ling;
   
   /* This should really pass things on to the other levels. */
   if (level != SOL_SOCKET) return(-EOPNOTSUPP);
@@ -506,6 +513,19 @@
 			val=256;
 		sk->sndbuf=val;
 		return 0;
+	case SO_LINGER:
+		err=verify_area(VERIFY_READ,optval,sizeof(ling));
+		if(err)
+			return err;
+		memcpy_fromfs(&ling,optval,sizeof(ling));
+		if(ling.l_onoff==0)
+			sk->linger=0;
+		else
+		{
+			sk->lingertime=ling.l_linger;
+			sk->linger=1;
+		}
+		return 0;
 	case SO_RCVBUF:
 		if(val>32767)
 			val=32767;
@@ -555,7 +575,8 @@
   struct sock *sk;
   int val;
   int err;
-
+  struct linger ling;
+  
   /* This should really pass things on to the other levels. */
   if (level != SOL_SOCKET) return(-EOPNOTSUPP);
 
@@ -566,8 +587,8 @@
   }
 
   switch(optname) {
-	case SO_DEBUG:		/* not implemented. */
-		val = sk->debug;	/* No per socket debugging _YET_ */
+	case SO_DEBUG:		
+		val = sk->debug;
 		break;
 		
 	case SO_DONTROUTE:	/* One last option to implement */
@@ -578,6 +599,20 @@
 		val= sk->broadcast;
 		break;
 		
+	case SO_LINGER:
+		
+		err=verify_area(VERIFY_WRITE,optval,sizeof(ling));
+		if(err)
+			return err;
+		err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
+		if(err)
+			return err;
+		put_fs_long(sizeof(ling),(unsigned long *)optlen);
+		ling.l_onoff=sk->linger;
+		ling.l_linger=sk->lingertime;
+		memcpy_tofs(optval,&ling,sizeof(ling));
+		return 0;
+		
 	case SO_SNDBUF:
 		val=sk->sndbuf;
 		break;
@@ -774,6 +809,7 @@
   sk->ack_timed = 0;
   sk->send_tmp = NULL;
   sk->mss = 0; /* we will try not to send any packets smaller than this. */
+  sk->debug = 0;
 
   /* this is how many unacked bytes we will accept for this socket.  */
   sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
@@ -870,13 +906,17 @@
 	DPRINTF((DBG_INET, "sk->linger set.\n"));
 	sk->prot->close(sk, 0);
 	cli();
-	while(sk->state != TCP_CLOSE) {
+	if (sk->lingertime)
+		current->timeout = jiffies + HZ*sk->lingertime;
+	while(sk->state != TCP_CLOSE && current->timeout>0) {
 		interruptible_sleep_on(sk->sleep);
 		if (current->signal & ~current->blocked) {
 			sti();
+			current->timeout=0;
 			return(-ERESTARTSYS);
 		}
 	}
+	current->timeout=0;
 	sti();
 	sk->dead = 1;
   }
@@ -990,7 +1030,15 @@
 	return(0);
   }
 
-  if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP)
+  if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+  {
+	sock->state = SS_CONNECTED;
+  /* Connection completing after a connect/EINPROGRESS/select/connect */
+	return 0;	/* Rock and roll */
+  }
+
+  if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP &&
+  	(flags & O_NONBLOCK))
   	return -EALREADY;	/* Connecting is currently in progress */
   	
   if (sock->state != SS_CONNECTING) {
@@ -1027,7 +1075,6 @@
 	   icmp error packets wanting to close a tcp or udp socket. */
 	if(sk->err && sk->protocol == IPPROTO_TCP)
 	{
-		sk->inuse=1;	/* keep the socket safe for us to use */
 		sti();
 		sock->state = SS_UNCONNECTED;
 		return -sk->err; /* set by tcp_err() */
@@ -1603,7 +1650,7 @@
 	return;
   }
   if (!sk->prot) {
-	printk("sock.c: release_sock sk->prot == NULL\n");
+/*	printk("sock.c: release_sock sk->prot == NULL\n"); */
 	return;
   }
 
@@ -1726,7 +1773,7 @@
   struct inet_protocol *p;
   int i;
 
-  printk("Swansea University Computer Society Net2Debugged [1.22]\n");
+  printk("Swansea University Computer Society Net2Debugged [1.24]\n");
   /* Set up our UNIX VFS major device. */
   if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) {
 	printk("%s: cannot register major device %d!\n",
@@ -1751,7 +1798,7 @@
 
 	tmp = (struct inet_protocol *) p->next;
 	inet_add_protocol(p);
-	printk("%s%s ",p->name,tmp?",":"\n");
+	printk("%s%s",p->name,tmp?", ":"\n");
 	p = tmp;
   }
 
diff --git a/net/inet/sock.h b/net/inet/sock.h
index bb3eeec..0b7736a 100644
--- a/net/inet/sock.h
+++ b/net/inet/sock.h
@@ -76,6 +76,7 @@
 				exp_growth,
 				zapped,	/* In ipx means not linked */
 				broadcast;
+  unsigned long		        lingertime;
   int				proc;
   struct sock			*next;
   struct sock			*pair;
diff --git a/net/inet/tcp.c b/net/inet/tcp.c
index 04701ad..83dfb41 100644
--- a/net/inet/tcp.c
+++ b/net/inet/tcp.c
@@ -37,6 +37,16 @@
  *		Alan Cox	:	Fixed another acking RST frame bug. Should stop
  *					LAN workplace lockups.
  *		Alan Cox	: 	Some tidyups using the new skb list facilities
+ *		Alan Cox	:	sk->keepopen now seems to work
+ *		Alan Cox	:	Pulls options out correctly on accepts
+ *		Alan Cox	:	Fixed assorted sk->rqueue->next errors
+ *		Alan Cox	:	PSH doesn't end a TCP read. Switched a bit to skb ops.
+ *		Alan Cox	:	Tidied tcp_data to avoid a potential nasty.
+ *		Alan Cox	:	Added some beter commenting, as the tcp is hard to follow
+ *		Alan Cox	:	Removed incorrect check for 20 * psh
+ *	Michael O'Reilly	:	ack < copied bug fix.
+ *	Johannes Stille		:	Misc tcp fixes (not all in yet).
+ *		Alan Cox	:	FIN with no memory -> CRASH
  *
  *
  * To Fix:
@@ -100,7 +110,7 @@
 
   printk("TCP header:\n");
   ptr =(unsigned char *)(th + 1);
-  printk("    source=%d, dest=%d, seq =%d, ack_seq = %d\n",
+  printk("    source=%d, dest=%d, seq =%ld, ack_seq = %ld\n",
 	ntohs(th->source), ntohs(th->dest),
 	ntohl(th->seq), ntohl(th->ack_seq));
   printk("    fin=%d, syn=%d, rst=%d, psh=%d, ack=%d, urg=%d res1=%d res2=%d\n",
@@ -113,6 +123,7 @@
  }
 
 
+
 /* This routine grabs the first thing off of a rcv queue. */
 static struct sk_buff *
 get_firstr(struct sock *sk)
@@ -120,6 +131,9 @@
   return skb_dequeue(&sk->rqueue);
 }
 
+/*
+ *	Difference between two values in tcp ack terms.
+ */
 
 static long
 diff(unsigned long seq1, unsigned long seq2)
@@ -135,8 +149,8 @@
 
 
 /* Enter the time wait state. */
-static void
-tcp_time_wait(struct sock *sk)
+
+static void tcp_time_wait(struct sock *sk)
 {
   sk->state = TCP_TIME_WAIT;
   sk->shutdown = SHUTDOWN_MASK;
@@ -145,6 +159,12 @@
   reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
 }
 
+/*
+ *	A timer event has trigger a tcp retransmit timeout. The
+ *	socket xmit queue is ready and set up to send. Because
+ *	the ack receive code keeps the queue straight we do
+ *	nothing clever here.
+ */
 
 static void
 tcp_retransmit(struct sock *sk, int all)
@@ -224,6 +244,11 @@
 }
 
 
+/*
+ *	Walk down the receive queue counting readable data until we hit the end or we find a gap
+ *	in the received data queue (ie a frame missing that needs sending to us)
+ */
+
 static int
 tcp_readable(struct sock *sk)
 {
@@ -232,43 +257,66 @@
   struct sk_buff *skb;
   int count=0;
   int sum;
+  unsigned long flags;
 
   DPRINTF((DBG_TCP, "tcp_readable(sk=%X)\n", sk));
+  if(sk && sk->debug)
+  	printk("tcp_readable: %p - ",sk);
 
-  if (sk == NULL || sk->rqueue == NULL) 
+  if (sk == NULL || skb_peek(&sk->rqueue) == NULL) 	/* Empty sockets are easy! */
+  {
+  	if(sk && sk->debug) 
+  		printk("empty\n");
   	return(0);
-
-  counted = sk->copied_seq+1;
+  }
+  
+  counted = sk->copied_seq+1;	/* Where we are at the moment */
   amount = 0;
-  skb =(struct sk_buff *)sk->rqueue->next;
+  
+  save_flags(flags);		/* So nobody adds things at the wrong moment */
+  cli();
+  skb =(struct sk_buff *)sk->rqueue;
 
   /* Do until a push or until we are out of data. */
   do {
 	count++;
+#ifdef OLD	
+	/* This is wrong: It breaks Chameleon amongst other stacks */
 	if (count > 20) {
+		restore_flags(flags);
 		DPRINTF((DBG_TCP, "tcp_readable, more than 20 packets without a psh\n"));
-		DPRINTF((DBG_TCP, "possible read_queue corruption.\n"));
+		printk("tcp_read: possible read_queue corruption.\n");
 		return(amount);
 	}
-	if (before(counted, skb->h.th->seq)) break;
-	sum = skb->len -(counted - skb->h.th->seq);
+#endif	
+	if (before(counted, skb->h.th->seq)) 	/* Found a hole so stops here */
+		break;
+	sum = skb->len -(counted - skb->h.th->seq);	/* Length - header but start from where we are up to (avoid overlaps) */
 	if (skb->h.th->syn) sum++;
 	if (skb->h.th->urg) {
-		sum -= ntohs(skb->h.th->urg_ptr);
+		sum -= ntohs(skb->h.th->urg_ptr);	/* Dont count urg data */
 	}
-	if (sum >= 0) {
+	if (sum >= 0) {					/* Add it up, move on */
 		amount += sum;
 		if (skb->h.th->syn) amount--;
 		counted += sum;
 	}
-	if (amount && skb->h.th->psh) break;
-	skb =(struct sk_buff *)skb->next;
-  } while(skb != sk->rqueue->next);
+/*	if (amount && skb->h.th->psh) break;*/
+	skb =(struct sk_buff *)skb->next;		/* Move along */
+  } while(skb != sk->rqueue);
+  restore_flags(flags);
   DPRINTF((DBG_TCP, "tcp readable returning %d bytes\n", amount));
+  if(sk->debug)
+  	printk("got %lu bytes.\n",amount);
   return(amount);
 }
 
 
+/*
+ *	Wait for a TCP event. Note the oddity with SEL_IN and reading. The
+ *	listening socket has a receive queue of sockets to accept.
+ */
+
 static int
 tcp_select(struct sock *sk, int sel_type, select_table *wait)
 {
@@ -278,23 +326,35 @@
   sk->inuse = 1;
   switch(sel_type) {
 	case SEL_IN:
+		if(sk->debug)
+			printk("select in");
 		select_wait(sk->sleep, wait);
-		if (sk->rqueue != NULL) {
+		if(sk->debug)
+			printk("-select out");
+		if (skb_peek(&sk->rqueue) != NULL) {
 			if (sk->state == TCP_LISTEN || tcp_readable(sk)) {
 				release_sock(sk);
+				if(sk->debug)
+					printk("-select ok data\n");
 				return(1);
 			}
 		}
 		if (sk->err != 0)	/* Receiver error */
 		{
 			release_sock(sk);
+			if(sk->debug)
+				printk("-select ok error");
 			return(1);
 		}
 		if (sk->shutdown & RCV_SHUTDOWN) {
 			release_sock(sk);
+			if(sk->debug)
+				printk("-select ok down\n");
 			return(1);
 		} else {
 			release_sock(sk);
+			if(sk->debug)
+				printk("-select fail\n");
 			return(0);
 		}
 	case SEL_OUT:
@@ -364,11 +424,8 @@
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
 
-			amount = 0;
 			sk->inuse = 1;
-			if (sk->rqueue != NULL) {
-				amount = tcp_readable(sk);
-			}
+			amount = tcp_readable(sk);
 			release_sock(sk);
 			DPRINTF((DBG_TCP, "returning %d\n", amount));
 			err=verify_area(VERIFY_WRITE,(void *)arg,
@@ -388,10 +445,10 @@
 			 * some urgent data.
 			 */
 			sk->inuse = 1;
-			if (sk->rqueue != NULL) {
-				skb =(struct sk_buff *)sk->rqueue->next;
-				if (sk->copied_seq+1 == skb->h.th->seq &&
-					skb->h.th->urg) answ = 1;
+			if ((skb=skb_peek(&sk->rqueue)) != NULL) 
+			{
+				if (sk->copied_seq+1 == skb->h.th->seq && skb->h.th->urg) 
+						answ = 1;
 			}
 			release_sock(sk);
 			err=verify_area(VERIFY_WRITE,(void *) arg,
@@ -406,7 +463,7 @@
 			unsigned long amount;
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
-			amount = sk->prot->wspace(sk)/2;
+			amount = sk->prot->wspace(sk);
 			err=verify_area(VERIFY_WRITE,(void *)arg,
 						   sizeof(unsigned long));
 			if(err)
@@ -603,8 +660,8 @@
   t1->ack_seq = ntohl(ack);
   t1->doff = sizeof(*t1)/4;
   tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
-if (inet_debug == DBG_SLIP) printk("\rtcp_ack: seq %x ack %x\n",
-				   sequence, ack);
+  if (sk->debug)
+  	 printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
   sk->prot->queue_xmit(sk, dev, buff, 1);
 }
 
@@ -616,7 +673,7 @@
 
   /* FIXME: want to get rid of this. */
   memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
-  th->seq = ntohl(sk->send_seq);
+  th->seq = htonl(sk->send_seq);
   th->psh =(push == 0) ? 1 : 0;
   th->doff = sizeof(*th)/4;
   th->ack = 1;
@@ -624,9 +681,9 @@
   sk->ack_backlog = 0;
   sk->bytes_rcv = 0;
   sk->ack_timed = 0;
-  th->ack_seq = ntohl(sk->acked_seq);
+  th->ack_seq = htonl(sk->acked_seq);
   sk->window = sk->prot->rspace(sk);
-  th->window = ntohs(sk->window);
+  th->window = htons(sk->window);
 
   return(sizeof(*th));
 }
@@ -654,7 +711,7 @@
   sk->inuse=1;
   prot = sk->prot;
   while(len > 0) {
-	if (sk->err) {
+	if (sk->err) {			/* Stop on an error */
 		release_sock(sk);
 		if (copied) return(copied);
 		tmp = -sk->err;
@@ -671,6 +728,9 @@
 		return(-EPIPE);
 	}
 
+
+	/* Wait for a connection to finish. */
+	
 	while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) {
 		if (sk->err) {
 			release_sock(sk);
@@ -704,14 +764,6 @@
 			return(-EAGAIN);
 		}
 
-		/*
-		 * FIXME:
-		 * Now here is a race condition.
-		 * release_sock could cause the connection to enter the
-		 * `established' mode, if that is the case, then we will
-		 * block here for ever, because we will have gotten our
-		 * wakeup call before we go to sleep.
-		 */
 		release_sock(sk);
 		cli();
 		if (sk->state != TCP_ESTABLISHED &&
@@ -762,6 +814,8 @@
   /*
    * We also need to worry about the window.
    * The smallest we will send is about 200 bytes.
+   * This is a bit sad for TCP/AMPR people running
+   * 196 byte windows! - FIXME
    */
   copy = min(sk->mtu, diff(sk->window_seq, sk->send_seq));
 
@@ -793,7 +847,7 @@
 
 	/* If we didn't get any memory, we need to sleep. */
 	if (skb == NULL) {
-		if (nonblock || copied) {
+		if (nonblock /* || copied */) {
 			release_sock(sk);
 			DPRINTF((DBG_TCP, "tcp_write: return 4\n"));
 			if (copied) return(copied);
@@ -893,6 +947,10 @@
 	}
   }
   sk->err = 0;
+  /* Avoid possible race on send_tmp - c/o Johannes Stille */
+  if(sk->send_tmp && !sk->packets_out)
+  	tcp_send_partial(sk);
+  /* -- */
   release_sock(sk);
   DPRINTF((DBG_TCP, "tcp_write: return 8\n"));
   return(copied);
@@ -989,30 +1047,33 @@
 static void
 cleanup_rbuf(struct sock *sk)
 {
+  unsigned long flags;
   int left;
+  struct sk_buff *skb;
 
-  DPRINTF((DBG_TCP, "cleaning rbuf for sk=%X\n", sk));
+  if(sk->debug)
+  	printk("cleaning rbuf for sk=%p\n", sk);
+  
+  save_flags(flags);
+  cli();
+  
   left = sk->prot->rspace(sk);
  
   /*
    * We have to loop through all the buffer headers,
    * and try to free up all the space we can.
    */
-  while(sk->rqueue != NULL ) {
-	struct sk_buff *skb;
-
-	skb =(struct sk_buff *)sk->rqueue->next;
-	if (!skb->used) break;
-	if (sk->rqueue == skb) {
-		sk->rqueue = NULL;
-	} else {
-		skb->next->prev = skb->prev;
-		skb->prev->next = skb->next;
-	}
+  while((skb=skb_peek(&sk->rqueue)) != NULL ) 
+  {
+	if (!skb->used) 
+		break;
+	skb_unlink(skb);
 	skb->sk = sk;
 	kfree_skb(skb, FREE_READ);
   }
 
+  restore_flags(flags);
+
   /*
    * FIXME:
    * At this point we should send an ack if the difference
@@ -1022,7 +1083,11 @@
   DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n",
 			sk->window - sk->bytes_rcv, sk->prot->rspace(sk)));
 
-  if (sk->prot->rspace(sk) != left) {
+  if(sk->debug)
+  	printk("sk->rspace = %lu, was %d\n", sk->prot->rspace(sk),
+  					    left);
+  if (sk->prot->rspace(sk) != left) 
+  {
 	/*
 	 * This area has caused the most trouble.  The current strategy
 	 * is to simply do nothing if the other end has room to send at
@@ -1060,9 +1125,10 @@
   DPRINTF((DBG_TCP, "tcp_read_urg(sk=%X, to=%X, len=%d, flags=%X)\n",
 					sk, to, len, flags));
 
-  while(len > 0) {
+  while(len > 0) 
+  {
 	sk->inuse = 1;
-	while(sk->urg==0 || sk->rqueue == NULL) {
+	while(sk->urg==0 || skb_peek(&sk->rqueue) == NULL) {
 		if (sk->err) {
 			int tmp;
 
@@ -1085,7 +1151,8 @@
 		 
 		if (sk->shutdown & RCV_SHUTDOWN) {
 			release_sock(sk);
-			if (copied == 0) sk->done = 1;
+			if (copied == 0) 
+				sk->done = 1;
 			return(copied);
 		}
 
@@ -1098,7 +1165,7 @@
 		/* Now at this point, we may have gotten some data. */
 		release_sock(sk);
 		cli();
-		if ((sk->urg == 0 || sk->rqueue == NULL) &&
+		if ((sk->urg == 0 || skb_peek(&sk->rqueue) == NULL) &&
 		    sk->err == 0 && !(sk->shutdown & RCV_SHUTDOWN)) {
 			interruptible_sleep_on(sk->sleep);
 			if (current->signal & ~current->blocked) {
@@ -1111,7 +1178,7 @@
 		sti();
 	}
 
-	skb =(struct sk_buff *)sk->rqueue->next;
+	skb = skb_peek(&sk->rqueue);
 	do {
 		int amt;
 
@@ -1136,7 +1203,7 @@
 			return(copied);
 		}
 		skb =(struct sk_buff *)skb->next;
-	} while(skb != sk->rqueue->next);
+	} while(skb != sk->rqueue);
   }
   sk->urg = 0;
   release_sock(sk);
@@ -1168,28 +1235,33 @@
   if (sk->state == TCP_LISTEN) return(-ENOTCONN);
 
   /* Urgent data needs to be handled specially. */
-  if ((flags & MSG_OOB)) return(tcp_read_urg(sk, nonblock, to, len, flags));
+  if ((flags & MSG_OOB)) 
+  	return(tcp_read_urg(sk, nonblock, to, len, flags));
 
   /* So no-one else will use this socket. */
   sk->inuse = 1;
-  if (sk->rqueue != NULL) skb =(struct sk_buff *)sk->rqueue->next;
-    else skb = NULL;
+  
+  skb=skb_peek(&sk->rqueue);
 
   DPRINTF((DBG_TCP, "tcp_read(sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n",
 						sk, to, len, nonblock, flags));
 
   while(len > 0) {
 	/* skb->used just checks to see if we've gone all the way around. */
+	
+	/* While no data, or first data indicates some is missing, or data is used */
 	while(skb == NULL ||
 	      before(sk->copied_seq+1, skb->h.th->seq) || skb->used) {
 		DPRINTF((DBG_TCP, "skb = %X:\n", skb));
 		cleanup_rbuf(sk);
-		if (sk->err) {
+		if (sk->err) 
+		{
 			int tmp;
 
 			release_sock(sk);
-			if (copied) {
-				DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+			if (copied) 
+			{
+				DPRINTF((DBG_TCP, "tcp_read: returning %d\n",
 									copied));
 				return(copied);
 			}
@@ -1198,10 +1270,11 @@
 			return(tmp);
 		}
 
-		if (sk->state == TCP_CLOSE) {
+		if (sk->state == TCP_CLOSE) 
+		{
 			release_sock(sk);
 			if (copied) {
-				DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+				DPRINTF((DBG_TCP, "tcp_read: returning %d\n",
 								copied));
 				return(copied);
 			}
@@ -1212,26 +1285,32 @@
 			return(-ENOTCONN);
 		}
 
-		if (sk->shutdown & RCV_SHUTDOWN) {
+		if (sk->shutdown & RCV_SHUTDOWN) 
+		{
 			release_sock(sk);
 			if (copied == 0) sk->done = 1;
-			DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
+			DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied));
 			return(copied);
 		}
 			
-		if (nonblock || copied) {
+		if (nonblock || copied) 
+		{
 			release_sock(sk);
-			if (copied) {
-				DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+			if(sk->debug)
+				printk("read: EAGAIN\n");
+			if (copied) 
+			{
+				DPRINTF((DBG_TCP, "tcp_read: returning %d\n",
 								copied));
 				return(copied);
 			}
 			return(-EAGAIN);
 		}
 
-		if ((flags & MSG_PEEK) && copied != 0) {
+		if ((flags & MSG_PEEK) && copied != 0) 
+		{
 			release_sock(sk);
-			DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
+			DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied));
 			return(copied);
 		}
 		 
@@ -1250,13 +1329,17 @@
 			continue;
 		}
 
-		if (sk->rqueue == NULL ||
-		    before(sk->copied_seq+1, sk->rqueue->next->h.th->seq)) {
+		if (skb_peek(&sk->rqueue) == NULL ||
+		    before(sk->copied_seq+1, sk->rqueue->h.th->seq)) {
+		        if(sk->debug)
+		        	printk("Read wait sleep\n");
 			interruptible_sleep_on(sk->sleep);
+			if(sk->debug)
+				printk("Read wait wakes\n");
 			if (current->signal & ~current->blocked) {
 				sti();
 				if (copied) {
-					DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+					DPRINTF((DBG_TCP, "tcp_read: returning %d\n",
 								copied));
 					return(copied);
 				}
@@ -1268,9 +1351,8 @@
 		DPRINTF((DBG_TCP, "tcp_read woke up. \n"));
 
 
-		if (sk->rqueue == NULL) skb = NULL;
-		  else skb =(struct sk_buff *)sk->rqueue->next;
-
+		skb=skb_peek(&sk->rqueue);
+		/* That may have been null if we were beaten, if so we loop again */
 	}
 
 	/*
@@ -1280,35 +1362,46 @@
 	 offset = sk->copied_seq+1 - skb->h.th->seq;
   
 	 if (skb->h.th->syn) offset--;
-	 if (offset < skb->len) {
+	 if (offset < skb->len) /* Some of the packet is useful */
+	 {
 		/*
 		 * If there is urgent data we must either
 		 * return or skip over it.
 		 */
-		if (skb->h.th->urg) {
-			if (skb->urg_used) {
+		if (skb->h.th->urg) 
+		{
+			if (skb->urg_used) 
+			{
 				sk->copied_seq += ntohs(skb->h.th->urg_ptr);
 				offset += ntohs(skb->h.th->urg_ptr);
-				if (offset >= skb->len) {
+				if (offset >= skb->len) 
+				{
 					skb->used = 1;
 					skb =(struct sk_buff *)skb->next;
 					continue;
 				}
-			} else {
+			} 
+			else 
+			{
 				release_sock(sk);
-				if (copied) return(copied);
+				if (copied) 
+					return(copied);
 				send_sig(SIGURG, current, 0);
 				return(-EINTR);
 			}
 		}
+		/* Ok so how much can we use ? */
 		used = min(skb->len - offset, len);
-/*		verify_area(VERIFY_WRITE, to, used); */
+		/* Copy it */
 		memcpy_tofs(to,((unsigned char *)skb->h.th) +
 			    skb->h.th->doff*4 + offset, used);
 		copied += used;
 		len -= used;
 		to += used;
-		if (!(flags & MSG_PEEK)) sk->copied_seq += used;
+		
+		/* If we were reading the data is 'eaten' */
+		if (!(flags & MSG_PEEK)) 
+			sk->copied_seq += used;
 	      
 		/*
 		 * Mark this data used if we are really reading it,
@@ -1317,24 +1410,31 @@
 		 */
 		if (!(flags & MSG_PEEK) &&
 		   (!skb->h.th->urg || skb->urg_used) &&
-		   (used + offset >= skb->len)) skb->used = 1;
+		   (used + offset >= skb->len)) 
+		   	skb->used = 1;
 	      
 		/*
 		 * See if this is the end of a message or if the
 		 * remaining data is urgent.
 		 */
-		if (skb->h.th->psh || skb->h.th->urg) {
+		if (/*skb->h.th->psh || */skb->h.th->urg) 
+		{
 			break;
 		}
-	} else {	/* already used this data, must be a retransmit */
+	} 
+	else 
+	{	/* already used this data, must be a retransmit */
 		skb->used = 1;
 	}
+	/* Move along a packet */
 	skb =(struct sk_buff *)skb->next;
   }
+  /* Clean up data we have read: This will do ACK frames */
   cleanup_rbuf(sk);
   release_sock(sk);
-  DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
-  if (copied == 0 && nonblock) return(-EAGAIN);
+  DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied));
+  if (copied == 0 && nonblock) 
+  	return(-EAGAIN);
   return(copied);
 }
 
@@ -1386,6 +1486,7 @@
 			   IPPROTO_TCP, sk->opt,
 			   sizeof(struct tcphdr));
   if (tmp < 0) {
+  	buff->free=1;
 	prot->wfree(sk,buff->mem_addr, buff->mem_len);
 	release_sock(sk);
 	DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
@@ -1412,6 +1513,7 @@
    * It should go at the end of the write queue.
    */
   if (sk->wback != NULL) {
+  	buff->free=0;	
 	buff->next = NULL;
 	sk->wback->next = buff;
 	sk->wback = buff;
@@ -1535,6 +1637,57 @@
 
 
 /*
+ *	Look for tcp options. Parses everything but only knows about MSS
+ */
+ 
+static void
+tcp_options(struct sock *sk, struct tcphdr *th)
+{
+  unsigned char *ptr;
+  int length=(th->doff*4)-sizeof(struct tcphdr);
+  int mtuset=0;
+    
+  ptr = (unsigned char *)(th + 1);
+  
+  while(length>0)
+  {
+  	int opcode=*ptr++;
+  	int opsize=*ptr++;
+  	switch(opcode)
+  	{
+  		case TCPOPT_EOL:
+  			return;
+  		case TCPOPT_NOP:
+  			length-=2;
+  			continue;
+  		
+  		default:
+  			if(opsize<=2)	/* Avoid silly options looping forever */
+  				return;
+  			switch(opcode)
+  			{
+  				case TCPOPT_MSS:
+  					if(opsize==4)
+  					{
+  						sk->mtu=min(sk->mtu,ntohs(*(unsigned short *)ptr));
+  						mtuset=1;
+  					}
+  					break;
+  				/* Add other options here as people feel the urge to implement stuff like large windows */
+  			}
+  			ptr+=opsize-2;
+  			length-=opsize;
+  	}
+  }
+  					
+  if (!mtuset) 
+  {
+	sk->mtu = min(sk->mtu, 576 - HEADER_SIZE);
+	return;
+  }
+}
+
+/*
  * This routine handles a connection request.
  * It should make sure we haven't already responded.
  * Because of the way BSD works, we have to send a syn/ack now.
@@ -1649,6 +1802,7 @@
   newsk->acked_seq = skb->h.th->seq + 1;
   newsk->copied_seq = skb->h.th->seq;
 
+#ifdef OLDWAY
   if (skb->h.th->doff == 5) {
 	newsk->mtu = dev->mtu - HEADER_SIZE;
   } else {
@@ -1660,7 +1814,9 @@
 			         dev->mtu - HEADER_SIZE);
 	}
   }
-
+#else
+  tcp_options(newsk,skb->h.th);
+#endif
   buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
   if (buff == NULL) {
 	sk->err = -ENOMEM;
@@ -1732,18 +1888,6 @@
   newsk->rmem_alloc += skb->mem_len;
 
   skb_queue_tail(&sk->rqueue,skb);
-#ifdef OLDWAY
-  if (sk->rqueue == NULL) {
-	skb->next = skb;
-	skb->prev = skb;
-	sk->rqueue = skb;
-  } else {
-	skb->next = sk->rqueue;
-	skb->prev = sk->rqueue->prev;
-	sk->rqueue->prev = skb;
-	skb->prev->next = skb;
-  }
-#endif
   sk->ack_backlog++;
   release_sock(newsk);
 }
@@ -1771,11 +1915,12 @@
   if (!sk->dead) wake_up(sk->sleep);
 
   /* We need to flush the recv. buffs. */
-  if (sk->rqueue != NULL) {
+  if (skb_peek(&sk->rqueue) != NULL) 
+  {
 	struct sk_buff *skb;
+#ifdef OLD	
 	struct sk_buff *skb2;
-
-	skb = sk->rqueue;
+	skb = skb_peek(&sk->rqueue);
 	do {
 		skb2 =(struct sk_buff *)skb->next;
 		/* if there is some real unread data, send a reset. */
@@ -1785,6 +1930,18 @@
 		kfree_skb(skb, FREE_WRITE);
 		skb = skb2;
 	} while(skb != sk->rqueue);
+#else
+	if(sk->debug)
+		printk("Clean rcv queue\n");
+	while((skb=skb_dequeue(&sk->rqueue))!=NULL)
+	{
+		if(skb->len > 0 && after(skb->h.th->seq + skb->len + 1 , sk->copied_seq))
+				need_reset = 1;
+		kfree_skb(skb, FREE_READ);
+	}
+	if(sk->debug)
+		printk("Cleaned.\n");
+#endif
   }
   sk->rqueue = NULL;
 
@@ -1801,7 +1958,7 @@
 		reset_timer(sk, TIME_CLOSE, 4 * sk->rtt);
 		if (timeout) tcp_time_wait(sk);
 		release_sock(sk);
-		break;
+		return;	/* break causes a double release - messy */
 	case TCP_TIME_WAIT:
 		if (timeout) {
 		  sk->state = TCP_CLOSE;
@@ -1824,6 +1981,9 @@
 		buff = (struct sk_buff *) prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC);
 		if (buff == NULL) {
 			/* This will force it to try again later. */
+			/* Or it would have if someone released the socket
+			   first. Anyway it might work now */
+			release_sock(sk);
 			if (sk->state != TCP_CLOSE_WAIT)
 					sk->state = TCP_ESTABLISHED;
 			reset_timer(sk, TIME_CLOSE, 100);
@@ -2023,9 +2183,9 @@
 		skb->link3 = NULL;
 		if (after(skb->h.seq, sk->window_seq)) {
 			if (sk->packets_out > 0) sk->packets_out--;
-
 			/* We may need to remove this from the dev send list. */
 			if (skb->next != NULL) {
+#ifdef OLD_WAY			
 				int i;
 
 				if (skb->next != skb) {
@@ -2046,8 +2206,10 @@
 					if (skb->next == skb) arp_q = NULL;
 					  else arp_q = skb->next;
 				}
+#else
+				skb_unlink(skb);				
+#endif				
 			}
-
 			/* Now add it to the write_queue. */
 			skb->magic = TCP_WRITE_QUEUE_MAGIC;
 			if (wskb == NULL) {
@@ -2144,31 +2306,7 @@
 		}
 
 		/* We may need to remove this from the dev send list. */		
-#ifndef OLDWAY
 		skb_unlink(oskb);	/* Much easier! */
-#else
-		if (oskb->next != NULL) {
-			int i;
-
-			if (oskb->next != oskb) {
-				oskb->next->prev = oskb->prev;
-				oskb->prev->next = oskb->next;
-			}
-			for(i = 0; i < DEV_NUMBUFFS; i++) {
-				if (oskb->dev->buffs[i] == oskb) {
-					if (oskb== oskb->next)
-						oskb->dev->buffs[i]= NULL;
-					  else
-						oskb->dev->buffs[i] = oskb->next;
-					break;
-				}
-			}
-			if (arp_q == oskb) {
-				if (oskb == oskb->next) arp_q = NULL;
-				  else arp_q =(struct sk_buff *)oskb->next;
-			}
-		}
-#endif
 		sti();
 		oskb->magic = 0;
 		kfree_skb(oskb, FREE_WRITE); /* write. */
@@ -2194,7 +2332,10 @@
 		DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); 
 		if (!sk->dead) wake_up(sk->sleep);
 
-		delete_timer(sk);
+		if (sk->keepopen)
+			reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+		else
+			delete_timer(sk);
 	} else {
 		if (sk->state != (unsigned char) sk->keepopen) {
 			reset_timer(sk, TIME_WRITE,
@@ -2261,6 +2402,7 @@
 {
   struct sk_buff *skb1, *skb2;
   struct tcphdr *th;
+  int dup_dumped=0;
 
   th = skb->h.th;
   print_th(th);
@@ -2300,17 +2442,27 @@
   /* This should start at the last one, and then go around forwards. */
   if (sk->rqueue == NULL) {
 	DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
-
+#ifdef OLDWAY
 	sk->rqueue = skb;
 	skb->next = skb;
 	skb->prev = skb;
+	skb->list = &sk->rqueue;
+#else
+	skb_queue_head(&sk->rqueue,skb);
+#endif		
 	skb1= NULL;
   } else {
 	DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
-
-	for(skb1=sk->rqueue; ; skb1 =(struct sk_buff *)skb1->prev) {
-		DPRINTF((DBG_TCP, "skb1=%X\n", skb1));
-		DPRINTF((DBG_TCP, "skb1->h.th->seq = %d\n", skb1->h.th->seq));
+	for(skb1=sk->rqueue->prev; ; skb1 =(struct sk_buff *)skb1->prev) {
+		if(sk->debug)
+		{
+			printk("skb1=%p :", skb1);
+			printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq);
+			printk("skb->h.th->seq = %ld\n",skb->h.th->seq);
+			printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq,
+					sk->acked_seq);
+		}
+#ifdef OLD		
 		if (after(th->seq+1, skb1->h.th->seq)) {
 			skb->prev = skb1;
 			skb->next = skb1->next;
@@ -2328,6 +2480,27 @@
 					to ack stuff. */
 			break;
 		}
+#else
+		if (th->seq==skb1->h.th->seq && skb->len>= skb1->len)
+		{
+			skb_append(skb1,skb);
+			skb_unlink(skb1);
+			kfree_skb(skb1,FREE_READ);
+			dup_dumped=1;
+			skb1=NULL;
+			break;
+		}
+		if (after(th->seq+1, skb1->h.th->seq))
+		{
+			skb_append(skb1,skb);
+			break;
+		}
+		if (skb1 == sk->rqueue)
+		{
+			skb_queue_head(&sk->rqueue, skb);		
+			break;
+		}
+#endif		
 	}
 	DPRINTF((DBG_TCP, "skb = %X:\n", skb));
   }
@@ -2342,7 +2515,7 @@
   }
 
   /* Now figure out if we can ack anything. */
-  if (skb1 == NULL || skb1->acked || before(th->seq, sk->acked_seq+1)) {
+  if ((!dup_dumped && (skb1 == NULL || skb1->acked)) || before(th->seq, sk->acked_seq+1)) {
       if (before(th->seq, sk->acked_seq+1)) {
 		if (after(th->ack_seq, sk->acked_seq))
 					sk->acked_seq = th->ack_seq;
@@ -2355,7 +2528,7 @@
 		}
 	  
 		for(skb2 = (struct sk_buff *)skb->next;
-		    skb2 !=(struct sk_buff *) sk->rqueue->next;
+		    skb2 !=(struct sk_buff *) sk->rqueue;
 		    skb2 = (struct sk_buff *)skb2->next) {
 			if (before(skb2->h.th->seq, sk->acked_seq+1)) {
 				if (after(skb2->h.th->ack_seq, sk->acked_seq))
@@ -2388,6 +2561,8 @@
 /*			tcp_send_ack(sk->send_seq, sk->acked_seq,sk,th, saddr); */
 		} else {
 			sk->ack_backlog++;
+			if(sk->debug)
+				printk("Ack queued.\n");
 			reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
 		}
 	}
@@ -2404,7 +2579,7 @@
 	 * window.
 	 */
 	while (sk->prot->rspace(sk) < sk->mtu) {
-		skb1 = (struct sk_buff *)sk->rqueue;
+		skb1 = skb_peek(&sk->rqueue);
 		if (skb1 == NULL) {
 			printk("INET: tcp.c:tcp_data memory leak detected.\n");
 			break;
@@ -2414,6 +2589,9 @@
 		if (skb1->acked) {
 			break;
 		}
+		
+		skb_unlink(skb1);
+#ifdef OLDWAY		
 		if (skb1->prev == skb1) {
 			sk->rqueue = NULL;
 		} else {
@@ -2421,6 +2599,7 @@
 			skb1->next->prev = skb1->prev;
 			skb1->prev->next = skb1->next;
 		}
+#endif		
 		kfree_skb(skb1, FREE_READ);
 	}
 	tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
@@ -2433,6 +2612,8 @@
 
   /* Now tell the user we may have some data. */
   if (!sk->dead) {
+        if(sk->debug)
+        	printk("Data wakeup.\n");
 	wake_up(sk->sleep);
   } else {
 	DPRINTF((DBG_TCP, "data received on dead socket.\n"));
@@ -2626,6 +2807,7 @@
   buff->mem_len = MAX_SYN_SIZE;
   buff->len = 24;
   buff->sk = sk;
+  buff->free = 1;
   t1 = (struct tcphdr *)(buff + 1);
 
   /* Put in the IP header and routing stuff. */
@@ -2724,56 +2906,6 @@
 
 
 
-/*
- *	Look for tcp options. Parses everything but only knows about MSS
- */
- 
-static void
-tcp_options(struct sock *sk, struct tcphdr *th)
-{
-  unsigned char *ptr;
-  int length=(th->doff*4)-sizeof(struct tcphdr);
-  int mtuset=0;
-    
-  ptr = (unsigned char *)(th + 1);
-  
-  while(length>0)
-  {
-  	int opcode=*ptr++;
-  	int opsize=*ptr++;
-  	switch(opcode)
-  	{
-  		case TCPOPT_EOL:
-  			return;
-  		case TCPOPT_NOP:
-  			length-=2;
-  			continue;
-  		
-  		default:
-  			if(opsize<=2)	/* Avoid silly options looping forever */
-  				return;
-  			switch(opcode)
-  			{
-  				case TCPOPT_MSS:
-  					if(opsize==4)
-  					{
-  						sk->mtu=min(sk->mtu,ntohs(*(unsigned short *)ptr));
-  						mtuset=1;
-  					}
-  					break;
-  				/* Add other options here as people feel the urge to implement stuff like large windows */
-  			}
-  			ptr+=opsize-2;
-  			length-=opsize;
-  	}
-  }
-  					
-  if (!mtuset) 
-  {
-	sk->mtu = min(sk->mtu, 576 - HEADER_SIZE);
-	return;
-  }
-}
 
 
 int
@@ -3159,10 +3291,10 @@
 						return(0);
 					}
 			}
-			if (th->fin) tcp_fin(sk, th, saddr, dev);
 			if (tcp_data(skb, sk, saddr, len))
-				kfree_skb(skb, FREE_READ);
+						kfree_skb(skb, FREE_READ);
 
+			if (th->fin) tcp_fin(sk, th, saddr, dev);
 			release_sock(sk);
 			return(0);
 		}
diff --git a/net/inet/udp.c b/net/inet/udp.c
index 3575bea..78066c3 100644
--- a/net/inet/udp.c
+++ b/net/inet/udp.c
@@ -29,6 +29,7 @@
  *		Alan Cox	:	Using generic datagram code. Even smaller and the PEEK
  *					bug no longer crashes it.
  *		Fred Van Kempen	: 	Net2e support for sk->broadcast.
+ *		Alan Cox	:	Uses skb_free_datagram
  *
  * To Do:
  *		Verify all the error codes from UDP operations match the
@@ -261,13 +262,17 @@
 
   skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */
   skb->dev = dev;
-
+#ifdef OLD
   /*
    * This code used to hack in some form of fragmentation.
    * I removed that, since it didn't work anyway, and it made the
    * code a bad thing to read and understand. -FvK
    */
   if (len > dev->mtu) {
+#else
+  if (skb->len > 4095)
+  {
+#endif    
 	printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu);
 	sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
 	return(-EMSGSIZE);
@@ -471,8 +476,9 @@
   if(skb==NULL)
   	return er;
   copied = min(len, skb->len);
+
   /* FIXME : should use udp header size info value */
-  memcpy_tofs(to, skb->h.raw + sizeof(struct udphdr), copied);
+  skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
 
   /* Copy the address. */
   if (sin) {
@@ -483,8 +489,8 @@
 	addr.sin_addr.s_addr = skb->daddr;
 	memcpy_tofs(sin, &addr, sizeof(*sin));
   }
-
-  kfree_skb(skb, FREE_READ);
+  
+  skb_free_datagram(skb);
   release_sock(sk);
   return(copied);
 }
@@ -546,7 +552,8 @@
 
   uh = (struct udphdr *) skb->h.uh;
   sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
-  if (sk == NULL) {
+  if (sk == NULL) 
+  {
 	if (chk_addr(daddr) == IS_MYADDR) 
 	{
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
@@ -561,26 +568,25 @@
 	return(0);
   }
 
-  if (!redo) {
-	if (uh->check && udp_check(uh, len, saddr, daddr)) {
-		DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
-		skb->sk = NULL;
-		kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
-
-	skb->sk = sk;
-	skb->dev = dev;
-	skb->len = len;
-
-	/* These are supposed to be switched. */
-	skb->daddr = saddr;
-	skb->saddr = daddr;
-
+  if (uh->check && udp_check(uh, len, saddr, daddr)) {
+	DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_WRITE);
+	return(0);
   }
 
+  skb->sk = sk;
+  skb->dev = dev;
+  skb->len = len;
+
+/* These are supposed to be switched. */
+  skb->daddr = saddr;
+  skb->saddr = daddr;
+
+
   /* Charge it to the socket. */
-  if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
+  if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
+  {
 	skb->sk = NULL;
 	kfree_skb(skb, FREE_WRITE);
 	release_sock(sk);
diff --git a/net/socket.c b/net/socket.c
index 8c72377..412a7f4 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -7,28 +7,38 @@
  *		Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
+ * Fixes:
+ *		Anonymous	:	NOTSOCK/BADF cleanup. Error fix in
+ *					shutdown()
+ *		Alan Cox	:	verify_area() fixes
+ *
+ *
  *		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.
  */
-#include <asm/system.h>
-#include <asm/segment.h>
+
 #include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/stat.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
 #include <linux/termios.h>
 #include <linux/net.h>
 #include <linux/ddi.h>
-#include <stdarg.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
 
 #undef SOCK_DEBUG
+
 #ifdef SOCK_DEBUG
+#include <stdarg.h>
 #define DPRINTF(x) dprintf x
 #else
 #define DPRINTF(x) /**/
@@ -464,6 +474,7 @@
 {
   int fd1, fd2, i;
   struct socket *sock1, *sock2;
+  int er;
 
   DPRINTF((net_debug,
 	"NET: sock_socketpair: family = %d, type = %d, protocol = %d\n",
@@ -496,7 +507,9 @@
   sock1->state = SS_CONNECTED;
   sock2->state = SS_CONNECTED;
 
-  verify_area(VERIFY_WRITE, usockvec, 2 * sizeof(int));
+  er=verify_area(VERIFY_WRITE, usockvec, 2 * sizeof(int));
+  if(er)
+  	return er;
   put_fs_long(fd1, &usockvec[0]);
   put_fs_long(fd2, &usockvec[1]);
 
@@ -515,7 +528,9 @@
   int i;
 
   DPRINTF((net_debug, "NET: sock_bind: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || current->filp[fd] == NULL)
+								return(-EBADF);
+  if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
 	DPRINTF((net_debug, "NET: sock_bind: bind failed\n"));
 	return(i);
@@ -535,7 +550,9 @@
   struct socket *sock;
 
   DPRINTF((net_debug, "NET: sock_listen: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || current->filp[fd] == NULL)
+								return(-EBADF);
+  if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   if (sock->state != SS_UNCONNECTED) {
 	DPRINTF((net_debug, "NET: sock_listen: socket isn't unconnected\n"));
 	return(-EINVAL);
@@ -559,7 +576,10 @@
   int i;
 
   DPRINTF((net_debug, "NET: sock_accept: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, &file))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+								return(-EBADF);
+  
+  if (!(sock = sockfd_lookup(fd, &file))) return(-ENOTSOCK);
   if (sock->state != SS_UNCONNECTED) {
 	DPRINTF((net_debug, "NET: sock_accept: socket isn't unconnected\n"));
 	return(-EINVAL);
@@ -611,7 +631,10 @@
   int i;
 
   DPRINTF((net_debug, "NET: sock_connect: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, &file))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || (file=current->filp[fd]) == NULL)
+								return(-EBADF);
+  
+  if (!(sock = sockfd_lookup(fd, &file))) return(-ENOTSOCK);
   switch(sock->state) {
 	case SS_UNCONNECTED:
 		/* This is ok... continue with connect */
@@ -643,7 +666,9 @@
   struct socket *sock;
 
   DPRINTF((net_debug, "NET: sock_getsockname: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || current->filp[fd] == NULL)
+								return(-EBADF);
+  if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   return(sock->ops->getname(sock, usockaddr, usockaddr_len, 0));
 }
 
@@ -654,7 +679,9 @@
   struct socket *sock;
 
   DPRINTF((net_debug, "NET: sock_getpeername: fd = %d\n", fd));
-  if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || current->filp[fd] == NULL)
+			return(-EBADF);
+  if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   return(sock->ops->getname(sock, usockaddr, usockaddr_len, 1));
 }
 
@@ -782,8 +809,9 @@
 
   DPRINTF((net_debug, "NET: sock_shutdown(fd = %d, how = %d)\n", fd, how));
 
-  file = current->filp[fd];
-  if (fd < 0 || fd >= NR_OPEN || file == NULL) return(-EBADF);
+  if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+								return(-EBADF);
+
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
 
   return(sock->ops->shutdown(sock, how));
@@ -810,55 +838,76 @@
 asmlinkage int
 sys_socketcall(int call, unsigned long *args)
 {
+  int er;
   switch(call) {
 	case SYS_SOCKET:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_socket(get_fs_long(args+0),
 				   get_fs_long(args+1),
 				   get_fs_long(args+2)));
 	case SYS_BIND:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_bind(get_fs_long(args+0),
 				 (struct sockaddr *)get_fs_long(args+1),
 				 get_fs_long(args+2)));
 	case SYS_CONNECT:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_connect(get_fs_long(args+0),
 				    (struct sockaddr *)get_fs_long(args+1),
 				    get_fs_long(args+2)));
 	case SYS_LISTEN:
-		verify_area(VERIFY_WRITE, args, 2 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 2 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_listen(get_fs_long(args+0),
 				   get_fs_long(args+1)));
 	case SYS_ACCEPT:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_accept(get_fs_long(args+0),
 				   (struct sockaddr *)get_fs_long(args+1),
 				   (int *)get_fs_long(args+2)));
 	case SYS_GETSOCKNAME:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_getsockname(get_fs_long(args+0),
 					(struct sockaddr *)get_fs_long(args+1),
 					(int *)get_fs_long(args+2)));
 	case SYS_GETPEERNAME:
-		verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_getpeername(get_fs_long(args+0),
 					(struct sockaddr *)get_fs_long(args+1),
 					(int *)get_fs_long(args+2)));
 	case SYS_SOCKETPAIR:
-		verify_area(VERIFY_WRITE, args, 4 * sizeof(long));
+		er=verify_area(VERIFY_READ, args, 4 * sizeof(long));
+		if(er)
+			return er;
 		return(sock_socketpair(get_fs_long(args+0),
 				       get_fs_long(args+1),
 				       get_fs_long(args+2),
 				       (unsigned long *)get_fs_long(args+3)));
 	case SYS_SEND:
-		verify_area(VERIFY_WRITE, args, 4 * sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_send(get_fs_long(args+0),
 				 (void *)get_fs_long(args+1),
 				 get_fs_long(args+2),
 				 get_fs_long(args+3)));
 	case SYS_SENDTO:
-		verify_area(VERIFY_WRITE, args, 6 * sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_sendto(get_fs_long(args+0),
 				   (void *)get_fs_long(args+1),
 				   get_fs_long(args+2),
@@ -866,13 +915,17 @@
 				   (struct sockaddr *)get_fs_long(args+4),
 				   get_fs_long(args+5)));
 	case SYS_RECV:
-		verify_area(VERIFY_WRITE, args, 4 * sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_recv(get_fs_long(args+0),
 				 (void *)get_fs_long(args+1),
 				 get_fs_long(args+2),
 				 get_fs_long(args+3)));
 	case SYS_RECVFROM:
-		verify_area(VERIFY_WRITE, args, 6 * sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_recvfrom(get_fs_long(args+0),
 				     (void *)get_fs_long(args+1),
 				     get_fs_long(args+2),
@@ -880,18 +933,24 @@
 				     (struct sockaddr *)get_fs_long(args+4),
 				     (int *)get_fs_long(args+5)));
 	case SYS_SHUTDOWN:
-		verify_area(VERIFY_WRITE, args, 2* sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 2* sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_shutdown(get_fs_long(args+0),
 				     get_fs_long(args+1)));
 	case SYS_SETSOCKOPT:
-		verify_area(VERIFY_WRITE, args, 5*sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_setsockopt(get_fs_long(args+0),
 				       get_fs_long(args+1),
 				       get_fs_long(args+2),
 				       (char *)get_fs_long(args+3),
 				       get_fs_long(args+4)));
 	case SYS_GETSOCKOPT:
-		verify_area(VERIFY_WRITE, args, 5*sizeof(unsigned long));
+		er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
+		if(er)
+			return er;
 		return(sock_getsockopt(get_fs_long(args+0),
 				       get_fs_long(args+1),
 				       get_fs_long(args+2),
@@ -906,10 +965,13 @@
 static int
 net_ioctl(unsigned int cmd, unsigned long arg)
 {
+  int er;
   switch(cmd) {
 	case DDIOCSDBG:
-		verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
-		net_debug = get_fs_long((int *)arg);
+		er=verify_area(VERIFY_READ, (void *)arg, sizeof(long));
+		if(er)
+			return er;
+		net_debug = get_fs_long((long *)arg);
 		if (net_debug != 0 && net_debug != 1) {
 			net_debug = 0;
 			return(-EINVAL);
diff --git a/net/unix/Makefile b/net/unix/Makefile
index 3a2d10c..ceb8aad 100644
--- a/net/unix/Makefile
+++ b/net/unix/Makefile
@@ -21,13 +21,6 @@
 unix.o: $(OBJS)
 	$(LD) -r -o unix.o $(OBJS)
 
-subdirs: dummy
-	for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
-
-
-clean:
-	rm -f core *.o *.a *.s
-
 dep:
 	$(CPP) -M *.c > .depend
 
diff --git a/net/unix/proc.c b/net/unix/proc.c
index 19e0ab3..68bd106 100644
--- a/net/unix/proc.c
+++ b/net/unix/proc.c
@@ -15,6 +15,9 @@
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-kalrsruhe.de>
  *
+ * Fixes:
+ *		Andriews Brouwer	:	Comment errors
+ *
  *		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
@@ -42,7 +45,7 @@
 
   for(i = 0; i < NSOCKETS; i++) {
 	if (unix_datas[i].refcnt) {
-		pos += sprintf(pos, "%2d: %08X %08X %08X %04X %02X", i,
+		pos += sprintf(pos, "%2d: %08X %08X %08lX %04X %02X", i,
 			unix_datas[i].refcnt,
 			unix_datas[i].protocol,
 			unix_datas[i].socket->flags,
@@ -61,9 +64,9 @@
 		}
 
 		/*
-		 * Check wether buffer _may_ overflow in the next loop.
+		 * Check whether buffer _may_ overflow in the next loop.
 		 * Since sockets may have very very long paths, we make
-		 * PATH_MAX+100 the minimum space left for a new line.
+		 * PATH_MAX+80 the minimum space left for a new line.
 		 */
 		if (pos > buffer+PAGE_SIZE-80-PATH_MAX) {
 			printk("UNIX: netinfo: oops, too many sockets.\n");
diff --git a/net/unix/sock.c b/net/unix/sock.c
index eb82516..f4de8a8 100644
--- a/net/unix/sock.c
+++ b/net/unix/sock.c
@@ -10,15 +10,24 @@
  *		Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
+ * Fixes:
+ *		Alan Cox	:	Verify Area
+ *
+ * BUGS
+ *	Page faults on read while another process reads could lose data.
+ *	Page faults on write happen to interleave data (probably not allowed)
+ *	with any other simultaneous writers on the socket but dont cause harm.
+ *
+ *
  *		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.
  */
-#include <asm/system.h>
-#include <asm/segment.h>
+
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/major.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
@@ -33,9 +42,13 @@
 #include <linux/fs.h>
 #include <linux/ddi.h>
 #include <linux/malloc.h>
-#include <stdarg.h>
-#include "unix.h"
 
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <stdarg.h>
+
+#include "unix.h"
 
 struct unix_proto_data unix_datas[NSOCKETS];
 static int unix_debug = 0;
@@ -123,7 +136,7 @@
     else {
 	memcpy(buf, sockun->sun_path, sockaddr_len);
 	buf[sockaddr_len] = '\0';
-	printk("\"%s\"[%d]\n", buf, sockaddr_len + UN_PATH_OFFSET);
+	printk("\"%s\"[%lu]\n", buf, sockaddr_len + UN_PATH_OFFSET);
   }
 }
   
@@ -346,6 +359,7 @@
   struct unix_proto_data *upd = UN_DATA(sock);
   unsigned long old_fs;
   int i;
+  int er;
 
   dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len);
   if (sockaddr_len <= UN_PATH_OFFSET ||
@@ -357,7 +371,9 @@
 	printk("UNIX: bind: already bound!\n");
 	return(-EINVAL);
   }
-  verify_area(VERIFY_WRITE, umyaddr, sockaddr_len);
+  er=verify_area(VERIFY_WRITE, umyaddr, sockaddr_len);
+  if(er)
+  	return er;
   memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
   upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
   if (upd->sockaddr_un.sun_family != AF_UNIX) {
@@ -401,6 +417,7 @@
   struct inode *inode;
   unsigned long old_fs;
   int i;
+  int er;
 
   dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len);
 
@@ -412,7 +429,9 @@
   if (sock->state == SS_CONNECTING) return(-EINPROGRESS);
   if (sock->state == SS_CONNECTED) return(-EISCONN);
 
-  verify_area(VERIFY_WRITE, uservaddr, sockaddr_len);
+  er=verify_area(VERIFY_READ, uservaddr, sockaddr_len);
+  if(er)
+  	return er;
   memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
   sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
   if (sockun.sun_family != AF_UNIX) {
@@ -523,6 +542,7 @@
 {
   struct unix_proto_data *upd;
   int len;
+  int er;
 
   dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self");
   if (peer) {
@@ -534,11 +554,15 @@
   } else
 	upd = UN_DATA(sock);
 
-  verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len));
+  er=verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len));
+  if(er)
+  	return er;
   if ((len = get_fs_long(usockaddr_len)) <= 0) return(-EINVAL);
   if (len > upd->sockaddr_len) len = upd->sockaddr_len;
   if (len) {
-	verify_area(VERIFY_WRITE, usockaddr, len);
+	er=verify_area(VERIFY_WRITE, usockaddr, len);
+	if(er)
+		return er;
 	memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
   }
   put_fs_long(len, usockaddr_len);
@@ -552,6 +576,7 @@
 {
   struct unix_proto_data *upd;
   int todo, avail;
+  int er;
 
   if ((todo = size) <= 0) return(0);
   upd = UN_DATA(sock);
@@ -586,7 +611,8 @@
 	if (cando >(part = BUF_SIZE - upd->bp_tail)) cando = part;
 	dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n",
 	       					avail, todo, cando);
-	verify_area(VERIFY_WRITE, ubuf, cando);
+	if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0)
+		return er;
 	memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
 	upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1);
 	ubuf += cando;
@@ -608,6 +634,7 @@
 {
   struct unix_proto_data *pupd;
   int todo, space;
+  int er;
 
   if ((todo = size) <= 0) return(0);
   if (sock->state != SS_CONNECTED) {
@@ -660,7 +687,9 @@
 	if (cando >(part = BUF_SIZE - pupd->bp_head)) cando = part;
 	dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n",
 	       					space, todo, cando);
-	verify_area(VERIFY_WRITE, ubuf, cando);
+	er=verify_area(VERIFY_READ, ubuf, cando);
+	if(er)
+		return er;
 	memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
 	pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1);
 	ubuf += cando;
@@ -727,6 +756,7 @@
 unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
   struct unix_proto_data *upd, *peerupd;
+  int er;
 
   upd = UN_DATA(sock);
   peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
@@ -734,7 +764,9 @@
   switch(cmd) {
 	case TIOCINQ:
 		if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
-		verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+		er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+		if(er)
+			return er;
 		if (UN_BUF_AVAIL(upd) || peerupd)
 			put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg);
 		  else
@@ -742,7 +774,9 @@
 		break;
 	case TIOCOUTQ:
 		if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
-		verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+		er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+		if(er)
+			return er;
 		if (peerupd) put_fs_long(UN_BUF_SPACE(peerupd),
 				   		(unsigned long *)arg);
 		  else
@@ -780,6 +814,7 @@
 	 unsigned int cmd, unsigned long arg)
 {
   int minor, ret;
+  int er;
 
   dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg);
   minor = MINOR(inode->i_rdev);
@@ -788,7 +823,9 @@
   ret = -EINVAL;
   switch(cmd) {
 	case DDIOCSDBG:
-		verify_area(VERIFY_WRITE,(void *)arg, sizeof(int));
+		er=verify_area(VERIFY_READ,(void *)arg, sizeof(int));
+		if(er)
+			return er;
 		unix_debug = get_fs_long((int *)arg);
 		if (unix_debug != 0 && unix_debug != 1) {
 			unix_debug = 0;
diff --git a/net/unix/unix.h b/net/unix/unix.h
index a83c081..5b69801 100644
--- a/net/unix/unix.h
+++ b/net/unix/unix.h
@@ -22,9 +22,6 @@
  */
 
 
-#define AF_UNIX_MAJOR	17		/* UNIX VFS major number	*/
-
-
 #ifdef _LINUX_UN_H
 
 
diff --git a/zBoot/Makefile b/zBoot/Makefile
index 5768caa..f4fc25b 100644
--- a/zBoot/Makefile
+++ b/zBoot/Makefile
@@ -18,7 +18,7 @@
 all:	zSystem
 
 zSystem:	piggy.o $(zOBJECTS)
-		$(LD) $(LDFLAGS) -o zSystem $(zOBJECTS) piggy.o
+		$(LD) $(LDFLAGS) -o zSystem -T 1000 $(zOBJECTS) piggy.o
 
 head.o:	head.s
 
@@ -29,7 +29,4 @@
 		./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o
 
 $(SYSTEM):
-		cd ..;make tools/zSystem
-
-clean:
-	-rm -f *.o *.s zSystem core piggy.* xtract piggyback
+		$(MAKE) -C .. tools/zSystem
diff --git a/zBoot/head.S b/zBoot/head.S
index 0a40e8b..47cfd1c 100644
--- a/zBoot/head.S
+++ b/zBoot/head.S
@@ -7,9 +7,14 @@
 /*
  *  head.S contains the 32-bit startup code.
  *
- * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
+ * NOTE!!! Startup happens at absolute address 0x00001000, which is also where
  * the page directory will exist. The startup code will be overwritten by
  * the page directory.
+ *
+ * Page 0 is deliberately kept safe, since System Management Mode code in 
+ * laptops may need to access the BIOS data stored there.  This is also
+ * useful for future device drivers that either access the BIOS via VM86 
+ * mode.
  */
 .text