diff -u --recursive --new-file v1.3.41/linux/CREDITS linux/CREDITS
--- v1.3.41/linux/CREDITS	Fri Oct  6 14:35:13 1995
+++ linux/CREDITS	Wed Nov 15 08:48:18 1995
@@ -336,11 +336,11 @@
 S: Canada H7Y 1V9
 
 N: David Gentzel
-E: gentzel@nova.enet.dec.com
-D: BusLogic driver and original UltraStor driver
+E: gentzel@telerama.lm.com
+D: Original BusLogic driver and original UltraStor driver
 S: Whitfield Software Services
-S: 631 Idlewood Avenue
-S: Carnegie, Pennsylvania 15106-1126
+S: 600 North Bell Avenue, Suite 160
+S: Carnegie, Pennsylvania 15106-4304
 S: USA
 
 N: Philip Gladstone
@@ -1077,7 +1077,8 @@
 
 N: Leonard N. Zubkoff
 E: lnz@dandelion.com
-D: XFree86 and BusLogic driver additions
+D: BusLogic SCSI driver
+D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
 S: USA
diff -u --recursive --new-file v1.3.41/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.41/linux/Documentation/Configure.help	Sun Oct 29 11:38:47 1995
+++ linux/Documentation/Configure.help	Tue Nov 14 10:01:16 1995
@@ -748,14 +748,11 @@
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
-  This is support for the BusLogic family of SCSI host adaptors.  If
-  it doesn't work out of the box, you may have to change some settings
-  in drivers/scsi/buslogic.h. Please read the SCSI-HOWTO, available
-  via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
-  as a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+  This is support for BusLogic MultiMaster SCSI Host Adaptors.  Consult
+  the documentation in drivers/scsi/README.BusLogic for more information.
+  BusLogic FlashPoint SCSI Host Adapters are not supported by this driver.
+  If this driver does not work correctly without modification, please
+  consult the author.  This driver is not currently available as a module.
  
 EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support
 CONFIG_SCSI_EATA_DMA
diff -u --recursive --new-file v1.3.41/linux/Documentation/SMP.txt linux/Documentation/SMP.txt
--- v1.3.41/linux/Documentation/SMP.txt	Sun Oct 29 11:38:47 1995
+++ linux/Documentation/SMP.txt	Tue Nov 14 16:03:52 1995
@@ -3,9 +3,7 @@
 specification. 
 
 WARNING:
-	This is experimental. Back up your disks first. Build only
-with gcc2.5.8.
-
+	This is experimental. Back up your disks first.
 
 To fix:
 
@@ -27,4 +25,4 @@
 	nicely).
 o	486 startup code.
 o	How to handle mixed FPU/non FPU processors.
-o	Support 4Mb page mode again
+o	Support 4Mb page mode again	[TESTING]
diff -u --recursive --new-file v1.3.41/linux/Documentation/networking/net-modules.txt linux/Documentation/networking/net-modules.txt
--- v1.3.41/linux/Documentation/networking/net-modules.txt	Sun Sep  3 12:26:48 1995
+++ linux/Documentation/networking/net-modules.txt	Tue Nov 14 07:48:52 1995
@@ -38,14 +38,54 @@
 	--------------------------
 
 
+	8390 based Network Modules		(Paul Gortmaker, Nov 12, 1995)
+	--------------------------
+
+(Includes: smc-ultra, ne, wd, 3c503, hp, hp-plus, e2100 and ac3200)
+
+The 8390 series of network drivers now support multiple card systems without 
+reloading the same module multiple times (memory efficient!) This is done by 
+specifying multiple comma separated values, such as:
+
+	insmod 3c503.o io=0x280,0x300,0x330,0x350  xcvr=0,1,0,1
+
+The above would have the one module controlling four 3c503 cards, with card 2
+and 4 using external transcievers. The "insmod" manual describes the usage
+of comma separated value lists.
+
+It is *STRONGLY RECOMMENDED* that you supply "io=" instead of autoprobing.
+If an "io=" argument is not supplied, then the ISA drivers will complain
+about autoprobing being not recommended, and begrudgingly autoprobe for
+a *SINGLE CARD ONLY* -- if you want to use multiple cards you *have* to 
+supply an "io=0xNNN,0xQQQ,..." argument.
+
+The ne module is an exception to the above. A NE2000 is essentially an
+8390 chip, some bus glue and some RAM. Because of this, the ne probe is
+more invasive than the rest, and so at boot we make sure the ne probe is 
+done last of all the 8390 cards (so that it won't trip over other 8390 based
+cards) With modules we can't ensure that all other non-ne 8390 cards have
+already been found. Because of this, the ne module REQUIRES an "io=0xNNN" 
+argument passed in via insmod. It will refuse to autoprobe.
+
+It is also worth noting that auto-IRQ probably isn't as reliable during 
+the flurry of interrupt activity on a running machine. Cards such as the 
+ne2000 that can't get the IRQ setting from an EEPROM or configuration
+register are probably best supplied with an "irq=M" argument as well.
+
+
+----------------------------------------------------------------------
+Card/Module List - Configurable Parameters and Default Values
+----------------------------------------------------------------------
+
 3c501.c:
 	io  = 0x280	IO base address
 	irq = 5		IRQ
 	(Probes ports:	0x280, 0x300)
 
 3c503.c:
-	io = 0x300
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ software selected by driver using autoIRQ)
+	xcvr = 0	(Use xcvr=1 to select external transceiver.)
 	(Probes ports: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2A0, 0x2E0)
 
 3c505.c:
@@ -69,8 +109,8 @@
 	(No public options, several other modules need this one)
 
 ac3200.c:
-	io = 0
-	irq = 0
+	io = 0		(Checks 0x1000 to 0x8fff in 0x1000 intervals)
+	irq = 0		(Read from config register)
 	(EISA probing..)
 
 apricot.c:
@@ -101,14 +141,10 @@
 	(Probes ports: 0x378, 0x278, 0x3BC;
 	 fixed IRQs: 5 and 7			)
 
-
-auto_irq.c: *Static kernel component*
-
-
 de4x5.c:
 	io = 0x000b
 	irq = 10
-	is_not_dec = 0  -- For SMC card using DEC 21140 set this to 1
+	is_not_dec = 0  -- For non-DEC card using DEC 21040/21041/21140 chip, set this to 1
 	(EISA, and PCI probing)
 
 de600.c:
@@ -130,8 +166,10 @@
 	No options
 
 e2100.c:
-	io = 0x300
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ software selected by driver)
+	mem = 0		(Override default shared memory start of 0xd0000)
+	xcvr = 0	(Use xcvr=1 to select external transceiver.)
 	(Probes ports: 0x300, 0x280, 0x380, 0x220)
 
 eepro.c:
@@ -158,13 +196,13 @@
 		0x300,        0x340, 0x360, 0x380, 0x3A0, 0x3C0)
 
 hp-plus.c:
-	io = 0x200
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ read from configuration register)
 	(Probes ports: 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340)
 
 hp.c:
-	io = 0x300
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ software selected by driver using autoIRQ)
 	(Probes ports: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240)
 
 hp100.c:
@@ -184,8 +222,8 @@
 loopback.c: *Static kernel component*
 
 ne.c:
-	io = 0x300
-	irq = 0
+	io = 0		(Explicitly *requires* an "io=0xNNN" value)
+	irq = 0		(Tries to determine configured IRQ via autoIRQ)
 	(Probes ports: 0x300, 0x280, 0x320, 0x340, 0x360)
 
 net_init.c: *Static kernel component*
@@ -225,8 +263,8 @@
 
 
 smc-ultra.c:
-	io = 0x200
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ val. read from EEPROM)
 	(Probes ports:	0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380)
 
 tulip.c: *Partial modularization*
@@ -240,11 +278,12 @@
 	irq = 0		(Not honoured, if changed..)
 
 wd.c:
-	io = 0x300
-	irq = 0
+	io = 0		(It will complain if you don't supply an "io=0xNNN")
+	irq = 0		(IRQ val. read from EEPROM, ancient cards use autoIRQ)
 	mem = 0		(Force shared-memory on address 0xC8000, or whatever..)
-	(Probes ports:	0x300, 0x280, 0x380, 0x240,
-	 uses AUTOIRQ)
+	mem_end = 0	(Force non-std. mem. size via supplying mem_end val.)
+			(eg. for 32k WD8003EBT, use mem=0xd0000 mem_end=0xd8000)
+	(Probes ports:	0x300, 0x280, 0x380, 0x240)
 
 znet.c: *Not modularized*
 	(Only one device on  Zenith Z-Note (notebook?) systems,
diff -u --recursive --new-file v1.3.41/linux/Makefile linux/Makefile
--- v1.3.41/linux/Makefile	Mon Nov 13 12:36:42 1995
+++ linux/Makefile	Tue Nov 14 16:03:52 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 41
+SUBLEVEL = 42
 
 ARCH = i386
 
@@ -9,8 +9,7 @@
 # because it makes re-config very ugly and too many fundamental files depend
 # on "CONFIG_SMP"
 #
-# NOTE! SMP is experimental, and gcc-2.5.8 is recommended. See the file
-# Documentation/SMP.
+# NOTE! SMP is experimental. See the file Documentation/SMP.txt
 #
 # SMP = 1
 
@@ -62,6 +61,15 @@
 ROOT_DEV = CURRENT
 
 #
+# NFS_ROOT_NAME specifies the default name of the directory to mount
+# as root via NFS, if the kernel does not get the "root=" option from
+# the boot loader. The "%s" will be replaced by the IP-number of the
+# local system.
+#
+
+NFS_ROOT = -DNFS_ROOT="\"/tftpboot/%s\""
+
+#
 # INSTALL_PATH specifies where to place the updated kernel and system map
 # images.  Uncomment if you want to place them anywhere other than root.
 
@@ -126,11 +134,22 @@
 
 include arch/$(ARCH)/Makefile
 
+ifdef SMP
+
+.S.s:
+	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -E -o $*.s $<
+.S.o:
+	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c -o $*.o $<
+
+else
+
 .S.s:
 	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $<
 .S.o:
 	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
+endif
+
 Version: dummy
 	@rm -f include/linux/compile.h
 
@@ -200,7 +219,7 @@
 	$(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
 
 init/main.o: init/main.c
-	$(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<
+	$(CC) $(CFLAGS) $(PROFILING) $(NFS_ROOT) -c -o $*.o $<
 
 fs: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=fs
diff -u --recursive --new-file v1.3.41/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v1.3.41/linux/arch/alpha/kernel/irq.c	Sun Oct 29 11:38:48 1995
+++ linux/arch/alpha/kernel/irq.c	Thu Nov 16 09:14:58 1995
@@ -352,8 +352,39 @@
 static inline void isa_device_interrupt(unsigned long vector,
 					struct pt_regs * regs)
 {
-	unsigned long pic;
+#if defined(CONFIG_ALPHA_APECS)
+#	define IACK_SC	APECS_IACK_SC
+#elif defined(CONFIG_ALPHA_LCA)
+#	define IACK_SC	LCA_IACK_SC
+#endif
 	int j;
+
+	/*
+	 * Generate a PCI interrupt acknowledge cycle.  The PIC will
+	 * respond with the interrupt vector of the highest priority
+	 * interrupt that is pending.  The PALcode sets up the
+	 * interrupts vectors such that irq level L generates vector
+	 * L.
+	 */
+	j = *(volatile int *) IACK_SC;
+	j &= 0xff;
+	if (j == 7) {
+	    if (!(inb(0x20) & 0x80)) {
+		/* it's only a passive release... */
+		return;
+	    }
+	}
+	device_interrupt(j, j, regs);
+#if 0
+	unsigned long pic;
+
+	/*
+	 * It seems to me that the probability of two or more *device*
+	 * interrupts occuring at almost exactly the same time is
+	 * pretty low.  So why pay the price of checking for
+	 * additional interrupts here if the common case can be
+	 * handled so much easier?
+	 */
 	/* 
 	 *  The first read of gives you *all* interrupting lines.
 	 *  Therefore, read the mask register and and out those lines
@@ -369,6 +400,7 @@
 		pic &= pic - 1;
 		device_interrupt(j, j, regs);
 	}
+#endif
 }
 
 static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
diff -u --recursive --new-file v1.3.41/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v1.3.41/linux/arch/i386/Makefile	Wed Oct  4 14:14:28 1995
+++ linux/arch/i386/Makefile	Tue Nov 14 16:03:52 1995
@@ -56,6 +56,10 @@
 endif
 endif
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 HEAD := arch/i386/kernel/head.o
 
 SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib
diff -u --recursive --new-file v1.3.41/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile
--- v1.3.41/linux/arch/i386/boot/Makefile	Thu Jun 29 19:02:39 1995
+++ linux/arch/i386/boot/Makefile	Tue Nov 14 16:03:52 1995
@@ -15,6 +15,10 @@
 CFLAGS := $(CFLAGS) -D__BFD__
 endif
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
 ifdef CONFIG_KERNEL_ELF
 	$(OBJDUMP) $(OBJDUMP_FLAGS) -o $(ZIMAGE_OFFSET) compressed/vmlinux > compressed/vmlinux.out
diff -u --recursive --new-file v1.3.41/linux/arch/i386/boot/bootsect.S linux/arch/i386/boot/bootsect.S
--- v1.3.41/linux/arch/i386/boot/bootsect.S	Wed Aug 10 19:23:01 1994
+++ linux/arch/i386/boot/bootsect.S	Tue Nov 14 07:38:19 1995
@@ -47,7 +47,7 @@
 #define RAMDISK 0
 #endif 
 #ifndef CONFIG_ROOT_RDONLY
-#define CONFIG_ROOT_RDONLY 0
+#define CONFIG_ROOT_RDONLY 1
 #endif
 
 ! ld86 requires an entry symbol. This may as well be the usual one.
diff -u --recursive --new-file v1.3.41/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile
--- v1.3.41/linux/arch/i386/boot/compressed/Makefile	Tue Aug 15 20:38:59 1995
+++ linux/arch/i386/boot/compressed/Makefile	Tue Nov 14 16:03:52 1995
@@ -11,6 +11,10 @@
 
 CFLAGS = -O2 -DSTDC_HEADERS
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 ifdef CONFIG_KERNEL_ELF
 TARGET=--target elf32-i386
 INPUT_DATA=input_data
@@ -22,8 +26,17 @@
 vmlinux: piggy.o $(OBJECTS)
 	$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o
 
+ifdef SMP
+
+head.o: head.S $(TOPDIR)/include/linux/tasks.h
+	$(CC) -D__SMP__ -traditional -c head.S
+
+else
+
 head.o: head.S $(TOPDIR)/include/linux/tasks.h
 	$(CC) -traditional -c head.S
+
+endif
 
 ifdef CONFIG_KERNEL_ELF
 
diff -u --recursive --new-file v1.3.41/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v1.3.41/linux/arch/i386/boot/setup.S	Wed Oct  4 14:14:28 1995
+++ linux/arch/i386/boot/setup.S	Tue Nov 14 15:56:35 1995
@@ -1,31 +1,32 @@
 !
 !	setup.S		Copyright (C) 1991, 1992 Linus Torvalds
 !
-! setup.s is responsible for getting the system data from the BIOS,
-! and putting them into the appropriate places in system memory.
-! both setup.s and system has been loaded by the bootblock.
-!
-! This code asks the bios for memory/disk/other parameters, and
-! puts them in a "safe" place: 0x90000-0x901FF, ie where the
-! boot-block used to be. It is then up to the protected mode
+! This code performs all initialization procedures that should be done
+! before entering the protected mode. It's responsible for getting of all
+! system data offered by BIOS and for detection / selection of video
+! mode. All information is put in a "safe" place: 0x90000-0x901FF, i. e.
+! where the boot-block used to be.  It is then up to the protected mode
 ! system to read them from there before the area is overwritten
 ! for buffer-blocks.
 !
 ! Move PS/2 aux init code to psaux.c
 ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
 !
-! some changes and additional features by Christoph Niemann,
+! Some changes and additional features by Christoph Niemann,
 ! March 1993/June 1994 (Christoph.Niemann@linux.org)
 !
+! Completely new video-mode handling code, VESA mode detection, support
+! for new Cirrus Logic cards and some additional changes
+! by Martin Mares <mj@k332.feld.cvut.cz>, October 1995.
+!
 
-! NOTE! These had better be the same as in bootsect.s!
+! NOTE! These had better be the same as in bootsect.S!
 #define __ASSEMBLY__
 #include <linux/config.h>
 #include <asm/segment.h>
 
-#ifndef SVGA_MODE
-#define SVGA_MODE ASK_VGA
-#endif
+! Uncomment this if you want the BIOS mode numbers to be listed
+!#define SHOW_BIOS_MODES
 
 ! Signature words to ensure LILO loaded us right
 #define SIG1	0xAA55
@@ -46,6 +47,7 @@
 
 entry start
 start:
+
 ! Bootlin depends on this being done early
 	mov	ax,#0x01500
 	mov	dl,#0x81
@@ -72,12 +74,14 @@
 ! Part of above routine, this one just prints ascii al
 
 prnt1:	push	ax
+	push	bx
 	push	cx
 	xor	bh,bh
 	mov	cx,#0x01
 	mov	ah,#0x0e
 	int	0x10
 	pop	cx
+	pop	bx
 	pop	ax
 	ret
 
@@ -145,43 +149,10 @@
 	xor	bx,bx		! clear bx
 	int	0x16
 
-! check for EGA/VGA and some config parameters
+! Check for video adapter and its parameters
+! Video mode selection is also handled here
 
-	mov	ah,#0x12
-	mov	bl,#0x10
-	int	0x10
-	mov	[8],ax
-	mov	[10],bx
-	mov	[12],cx
-	mov	ax,#0x5019
-	cmp	bl,#0x10
-	je	novga
-	mov	ax,#0x1a00	! Added check for EGA/VGA discrimination
-	int	0x10
-	mov	bx,ax
-	mov	ax,#0x5019
-	movb	[15],#0		! by default, no VGA
-	cmp	bl,#0x1a	! 1a means VGA, anything else EGA or lower
-	jne	novga
-	movb	[15],#1		! we've detected a VGA
-	call	chsvga
-novga:	mov	[14],al
-	mov	ah,#0x03	! read cursor pos
-	xor	bh,bh		! clear bh
-	int	0x10		! save it in known place, con_init fetches
-	mov	[0],dx		! it from 0x90000.
-	
-! Get video-card data:
-	
-	mov	ah,#0x0f
-	int	0x10
-	mov	[4],bx		! bh = display page
-	mov	[6],ax		! al = video mode, ah = window width
-	xor	ax,ax
-	mov	es,ax		! Access low memory
-	seg es
-	mov	ax,[0x485]	! POINTS - Height of character matrix
-	mov	[16],ax
+	call	video
 
 ! Get hd0 data
 
@@ -334,17 +305,16 @@
 ! Well, now's the time to actually move into protected mode. To make
 ! things as simple as possible, we do no register set-up or anything,
 ! we let the gnu-compiled 32-bit programs do that. We just jump to
-! absolute address 0x00000, in 32-bit protected mode.
+! absolute address 0x10000, in 32-bit protected mode.
 !
 ! Note that the short jump isn't strictly needed, although there are
 ! reasons why it might be a good idea. It won't hurt in any case.
 !
-	xor	ax,ax
-	inc	ax		! protected mode (PE) bit
+	mov	ax,#1		! protected mode (PE) bit
 	lmsw	ax		! This is it!
 	jmp	flush_instr
 flush_instr:
-	mov	bx,#0		! Flag to indicate a boot
+	xor	bx,bx		! Flag to indicate a boot
 	jmpi	0x1000,KERNEL_CS	! jmp offset 1000 of segment 0x10 (cs)
 
 ! This routine checks that the keyboard command queue is empty
@@ -364,61 +334,6 @@
 	test	al,#2		! is input buffer full?
 	jnz	empty_8042	! yes - loop
 	ret
-!
-! Read a key and return the (US-)ascii code in al, scan code in ah
-!
-getkey:
-	xor	ah,ah
-	int	0x16
-	ret
-
-!
-! Read a key with a timeout of 30 seconds. The cmos clock is used to get
-! the time.
-!
-getkt:
-	call	gettime
-	add	al,#30		! wait 30 seconds
-	cmp	al,#60
-	jl	lminute
-	sub	al,#60
-lminute:
-	mov	cl,al
-again:	mov	ah,#0x01
-	int	0x16
-	jnz	getkey		! key pressed, so get it
-	call	gettime
-	cmp	al,cl
-	jne	again
-	mov	al,#0x20	! timeout, return default char `space'
-	ret
-
-!
-! Flush the keyboard buffer
-!
-flush:	mov	ah,#0x01
-	int	0x16
-	jz	empty
-	xor	ah,ah
-	int	0x16
-	jmp	flush
-empty:	ret
-
-!
-! Read the cmos clock. Return the seconds in al
-!
-gettime:
-	push	cx
-	mov	ah,#0x02
-	int	0x1a
-	mov	al,dh			! dh contains the seconds
-	and	al,#0x0f
-	mov	ah,dh
-	mov	cl,#0x04
-	shr	ah,cl
-	aad
-	pop	cx
-	ret
 
 !
 ! Delay is needed after doing i/o
@@ -427,43 +342,199 @@
 	.word	0x00eb			! jmp $+2
 	ret
 
-! Routine trying to recognize type of SVGA-board present (if any)
-! and if it recognize one gives the choices of resolution it offers.
-! If one is found the resolution chosen is given by al,ah (rows,cols).
+!
+! Video card / mode detection. We do some hardware testing and build
+! a video mode list (placed directly after our code and data). Then we
+! choose the right mode given in the configuration or ask the user if
+! we are requested to do so. After all, all video parameters are stored
+! for later perusal by the kernel.
+!
 
-chsvga:	cld
+video:	movb	[15],#0		! Default is no VGA
+	mov	ax,[0x01fa]
+	push	ds
 	push	ds
+	pop	fs		! In this routine: FS=orig. DS
 	push	cs
-	mov	ax,[0x01fa]
-	pop	ds
+	pop	ds		! ES=DS=CS
+	push	cs
+	pop	es
 	mov	modesave,ax
-	mov 	ax,#0xc000
-	mov	es,ax
-	mov	ax,modesave
-	cmp	ax,#NORMAL_VGA
-	je	defvga
-	cmp	ax,#EXTENDED_VGA
-	je	vga50
+	lea	di,modelist	! ES:DI points to current item in our mode list
+	mov	eax,#0x50190000	! Store current mode: 80x25
+	cld
+	stosd
+
+	mov	ah,#0x12	! Check EGA/VGA
+	mov	bl,#0x10
+	int	0x10
+	seg	fs
+	mov	[10],bx		! Used for identification of VGA in the kernel
+	cmp	bl,#0x10	! Not EGA nor VGA -> 80x25 only
+	je	selmd1
+
+	mov	eax,#0x5032FFFF	! EGA or VGA: 80x50 supported
+	stosd
+
+	mov	ax,#0x1a00	! Added check for EGA/VGA discrimination
+	int	0x10
+	cmp	al,#0x1a	! 1a means VGA, anything else EGA
+	jne	selmd1
+	seg	fs
+	movb	[15],#1		! We've detected a VGA
+
+	mov	eax,#0x501cFFFE	! VGA: 80x28 supported
+	stosd
+
+	lea	si,vgatable	! Test all known SVGA adapters
+dosvga:	lodsw
+	mov	bp,ax		! Default mode table
+	or	ax,ax
+	jz	didsv
+	lodsw			! Pointer to test routine
+	push	si
+	push	di
+	push	es
+	mov	bx,#0xc000
+	mov	es,bx
+	call	ax		! Call test routine
+	pop	es
+	pop	di
+	pop	si
+	or	bp,bp
+	jz	dosvga
+	mov	si,bp		! Found, copy the modes
+	mov	ah,#0
+cpsvga:	lodsb
+	or	al,al
+	jz	didsv
+	stosw
+	movsw
+	jmp	cpsvga
+
+selmd1:	jmp	selmd
+
+didsv:	mov	ax,#0x4f00	! Fetch VESA information to ES:DI+0x400
+	add	di,#0x400
+	int	0x10
+	sub	di,#0x400
+	cmp	al,#0x4f
+	jne	selmd
+	lgs	bx,(di+0x40e)
+	cmp	(di+0x400),#0x4556
+	jne	selmd
+	cmp	(di+0x402),#0x4153
+	jne	selmd
+
+vesa1:	seg	gs
+	mov	cx,(bx)
+	add	bx,#2
+	cmp	cx,#0xffff
+	je	selmd
+	mov	ax,#0x4f01
+	add	di,#0xc00
+	int	0x10
+	sub	di,#0xc00
+	cmp	al,#0x4f
+	jne	selmd
+	testb	(di+0xc00),#0x10	! Is it a text mode?
+	jne	vesa1
+	testb	(di+0xc00),#0x08	! Has it colors?
+	je	vesa1
+	mov	dh,(di+0xc12)	! DX=dimensions, CX=mode
+	mov	dl,(di+0xc14)
+
+	lea	si,modelist	! Check if it's already on the list
+vesa2:	lodsw
+	lodsw
+	cmp	ax,dx
+	je	vesa1
+	cmp	si,di
+	jc	vesa2
+
+	mov	ax,cx		! New mode, store it
+	stosw
+	mov	ax,dx
+	stosw
+	jmp	vesa1
+
+!
+! Video mode table built. Determine the mode we should use and set it.
+!
+selmd:	mov	ax,modesave
+	cmp	ax,#NORMAL_VGA	! Current mode (80x25)
+	je	defmd1
+	cmp	ax,#EXTENDED_VGA ! 80x50 mode
+	je	try50
 	cmp	ax,#ASK_VGA
-	jne	svga
-	lea	si,msg1
+	jne	usemd
+banner:	lea	si,keymsg
 	call	prtstr
 	call	flush
 nokey:	call	getkt
-	cmp	al,#0x0d		! enter ?
-	je	svga			! yes - svga selection
-	cmp	al,#0x20		! space ?
-	je	defvga			! no - repeat
+	cmp	al,#0x0d	! ENTER ?
+	je	listm		! yes - manual mode selection
+	cmp	al,#0x20	! SPACE ?
+	je	defmd1		! no - repeat
 	call 	beep
 	jmp	nokey
-defvga:	mov	ax,#0x5019
-	pop	ds
-	ret
-/* extended vga mode: 80x50 */
-vga50:
-	mov	ax,#0x1112
+
+defmd1:	br	defmd
+
+listm:	call	listmodes	! List all available modes
+keymd:	call	getkey		! Get key representing mode ID
+	xor	ah,ah
+	sub	al,#0x30
+	jc	keymd
+	cmp	al,#10
+	jc	usemd
+	sub	al,#39
+	cmp	al,#10
+	jc	keymd
+	cmp	al,#26
+	jnc	keymd
+	jmp	usemd
+
+try50:	mov	ax,#1		! 80x50 is mode #1
+usemd:	shl	ax,#2		! We're requested to set mode in AX
+	lea	si,modelist
+	add	si,ax
+	cmp	si,di
+	jc	mdok
+	cmp	modesave,#ASK_VGA
+	je	keymd
+	lea	si,undefd
+	call	prtstr
+	jmp	banner
+
+mdok:	lodsw			! AX=mode number
+	cmp	ah,#0xff
+	jz	mdspec
+	or	ax,ax
+	jz	mdsetd
+	or	ah,ah
+	jz	mdset
+	mov	bx,ax
+	mov	ax,#0x4f02
+mdset:	int	0x10
+mdsetd:	lodsb			! AL=number of lines
+	jmp	getpar
+
+mdspec:	inc	ax		! Special modes
+	jz	m80x50
+
+m80x28: mov	ax,#0x1111	! Setting 80x28 (VGA with EGA font)
+	xor	bl,bl
+	int	0x10		! use 9x14 fontset (28 lines on VGA)
+	mov	ah,#0x01
+	mov	cx,#0x0b0c
+	int	0x10		! turn on cursor (scan lines 11 to 12)
+	mov	al,#28
+	jmp	getpar
+
+m80x50: mov	ax,#0x1112	! Setting 80x50 (EGA/VGA)
 	xor	bl,bl
-	int	0x10		! use 8x8 font set (50 lines on VGA)
+	int	0x10		! use 8x8 font set
 	mov	ax,#0x1200
 	mov	bl,#0x20
 	int	0x10		! use alternate print screen
@@ -473,28 +544,65 @@
 	mov	ah,#0x01
 	mov	cx,#0x0607
 	int	0x10		! turn on cursor (scan lines 6 to 7)
-	pop	ds
-	mov	ax,#0x5032	! return 80x50
-	ret
-/* extended vga mode: 80x28 */
-vga28:
-	pop	ax		! clean the stack
-	mov	ax,#0x1111
-	xor	bl,bl
-	int	0x10		! use 9x14 fontset (28 lines on VGA)
-	mov	ah, #0x01
-	mov	cx,#0x0b0c
-	int	0x10		! turn on cursor (scan lines 11 to 12)
-	pop	ds
-	mov	ax,#0x501c	! return 80x28
-	ret
-/* svga modes */
+	mov	al,#50
+	jmp	getpar
+
+defmd:	mov	al,#25		! Default is 25 lines
+
+!
+! Correct video mode set. Determine the remaining parameters.
+!
+
+getpar:	pop	ds		! Restore original DS
+	mov	[14],al		! Number of lines
+
+	mov	ah,#0x03	! read cursor pos
+	xor	bh,bh		! clear bh
+	int	0x10		! save it in known place, con_init fetches
+	mov	[0],dx		! it from 0x90000.
+	
+	mov	ah,#0x0f
+	int	0x10
+	mov	[4],bx		! bh = display page
+	mov	[6],ax		! al = video mode, ah = window width
+	xor	ax,ax
+	mov	es,ax		! Access low memory
+	seg es
+	mov	ax,[0x485]	! POINTS - Height of character matrix
+	mov	[16],ax
+
+	ret			! Well done...
+
 !
-!	test for presence of an S3 VGA chip. The algorithm was taken
-!	from the SuperProbe package of XFree86 1.2.1
-!	report bugs to Christoph.Niemann@linux.org
+! Table of all known SVGA cards. For each card, we store a pointer to
+! a table of video modes supported by the card and a pointer to a routine
+! used for testing of presence of the card.
 !
-svga:   cld
+
+vgatable:
+	.word	s3_md, s3_test
+	.word	ati_md, ati_test
+	.word	ahead_md, ahead_test
+	.word	chips_md, chips_test
+	.word	cirrus2_md, cirrus2_test
+	.word	cirrus1_md, cirrus1_test
+	.word	everex_md, everex_test
+	.word	genoa_md, genoa_test
+	.word	oak_md, oak_test
+	.word	paradise_md, paradise_test
+	.word	trident_md, trident_test
+	.word	tseng_md, tseng_test
+	.word	video7_md, video7_test
+	.word	0
+
+!
+! Test routines and mode tables:
+!
+
+! S3 - The test algorithm was taken from the SuperProbe package
+! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
+
+s3_test:
 	mov	cx,#0x0f35	! we store some constants in cl/ch
 	mov	dx,#0x03d4
 	movb	al,#0x38
@@ -559,29 +667,64 @@
 	repne
 	scasb
 	je	no_s31
-	lea 	si,dsc_S3	! table of descriptions of video modes for BIOS
-	lea	di,mo_S3	! table of sizes of video modes for my BIOS
 	movb	ah,bh
 	movb	al,#0x38
-	call	outidx		! restore old value of CRT register 0x38
-	br	selmod		! go ask for video mode
+	jmp	s3rest
 no_s3:	movb	al,#0x35	! restore CRT register 0x35
 	movb	ah,bl
 	call	outidx
-no_s31:	movb	ah,bh
-	movb	al,#0x38
-	call	outidx		! restore old value of CRT register 0x38
+no_s31:	xor	bp,bp		! Detection failed
+s3rest:	movb	ah,bh
+	movb	al,#0x38	! restore old value of CRT register 0x38
+outidx:	out	dx,al		! Write to indexed VGA register
+	push	ax		! AL=index, AH=data, DX=index reg port
+	mov	al,ah
+	inc	dx
+	out	dx,al
+	dec	dx
+	pop	ax
+	ret
 
-	lea 	si,idati		! Check ATI 'clues'
+tstidx:	out	dx,ax		! OUT DX,AX and inidx
+inidx:	out	dx,al		! Read from indexed VGA register
+	inc	dx		! AL=index, DX=index reg port -> AL=data
+	in	al,dx
+	dec	dx
+	ret
+
+idS3:	.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
+	.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
+
+s3_md:	.byte	0x54, 0x2b, 0x84
+	.byte	0x55, 0x19, 0x84
+	.byte	0
+
+! ATI cards.
+
+ati_test:
+	lea 	si,idati
 	mov	di,#0x31
 	mov 	cx,#0x09
 	repe
 	cmpsb
-	jne	noati
-	lea	si,dscati
-	lea	di,moati
-	br	selmod
-noati:	mov	ax,#0x200f		! Check Ahead 'clues'
+	je	atiok
+	xor	bp,bp
+atiok:	ret
+
+idati:	.ascii	"761295520"
+
+ati_md:	.byte	0x23, 0x19, 0x84
+	.byte	0x33, 0x2c, 0x84
+	.byte	0x22, 0x1e, 0x64
+	.byte	0x21, 0x19, 0x64
+	.byte	0x58, 0x21, 0x50
+	.byte	0x5b, 0x1e, 0x50
+	.byte	0
+
+! AHEAD
+
+ahead_test:
+	mov	ax,#0x200f
 	mov	dx,#0x3ce
 	out	dx,ax
 	inc	dx
@@ -589,11 +732,23 @@
 	cmp	al,#0x20
 	je	isahed
 	cmp	al,#0x21
-	jne	noahed
-isahed:	lea	si,dscahead
-	lea	di,moahead
-	br	selmod
-noahed:	mov	dx,#0x3c3		! Check Chips & Tech. 'clues'
+	je	isahed
+	xor	bp,bp
+isahed:	ret
+
+ahead_md:
+	.byte	0x22, 0x2c, 0x84
+	.byte	0x23, 0x19, 0x84
+	.byte	0x24, 0x1c, 0x84
+	.byte	0x2f, 0x32, 0xa0
+	.byte	0x32, 0x22, 0x50
+	.byte	0x34, 0x42, 0x50
+	.byte	0
+
+! Chips & Tech.
+
+chips_test:
+	mov	dx,#0x3c3
 	in	al,dx
 	or	al,#0x10
 	out	dx,al
@@ -604,12 +759,20 @@
 	in	al,dx
 	and	al,#0xef
 	out	dx,al
-	cmp	bl,[idcandt]
-	jne	nocant
-	lea	si,dsccandt
-	lea	di,mocandt
-	br	selmod
-nocant:	mov	dx,#0x3d4		! Check Cirrus 'clues'
+	cmp	bl,#0xa5
+	je	cantok
+	xor	bp,bp
+cantok:	ret
+
+chips_md:
+	.byte	0x60, 0x19, 0x84
+	.byte	0x61, 0x32, 0x84
+	.byte	0
+
+! Cirrus Logic 5X0
+
+cirrus1_test:
+	mov	dx,#0x3d4
 	mov	al,#0x0c
 	out	dx,al
 	inc	dx
@@ -642,19 +805,76 @@
 	out	dx,al
 	in	al,dx
 	cmp	al,#0x01
-	jne	nocirr
-	call	rst3d4	
-	lea	si,dsccirrus
-	lea	di,mocirrus
-	br	selmod
-rst3d4:	mov	dx,#0x3d4
+	je	iscirr
+nocirr:	xor	bp,bp
+iscirr: mov	dx,#0x3d4
 	mov	al,bl
 	xor	ah,ah
 	shl	ax,#8
 	add	ax,#0x0c
 	out	dx,ax
-	ret	
-nocirr:	call	rst3d4			! Check Everex 'clues'
+	ret
+
+cirrus1_md:
+	.byte	0x1f, 0x19, 0x84
+	.byte	0x20, 0x2c, 0x84
+	.byte	0x22, 0x1e, 0x84
+	.byte	0x31, 0x25, 0x64
+	.byte	0
+
+! Cirrus Logic 54XX
+
+cirrus2_test:
+	mov	dx,#0x3c4
+	mov	al,#6
+	call	inidx
+	mov	bl,al			! BL=backup
+	mov	al,#6
+	xor	ah,ah
+	call	tstidx
+	cmp	al,#0x0f
+	jne	c2fail
+	mov	ax,#0x1206
+	call	tstidx
+	cmp	al,#0x12
+	jne	c2fail
+	mov	al,#0x1e
+	call	inidx
+	mov	bh,al
+	and	bh,#0xc0
+	mov	ah,bh
+	mov	al,#0x1e
+	call	tstidx
+	xor	al,bh
+	and	al,#0x3f
+	jne	c2xx
+	mov	al,#0x1e
+	mov	ah,bh
+	or	ah,#0x3f
+	call	tstidx
+	xor	al,bh
+	xor	al,#0x3f
+	and	al,#0x3f
+c2xx:	pushf
+	mov	al,#0x1e
+	mov	ah,bh
+	out	dx,ax
+	popf
+	je	c2done
+c2fail:	xor	bp,bp
+c2done:	mov	al,#6
+	mov	ah,bl
+	out	dx,ax
+	ret
+
+cirrus2_md:
+	.byte	0x14, 0x19, 0x84
+	.byte	0x54, 0x2b, 0x84
+	.byte	0
+
+! Everex / Trident
+
+everex_test:
 	mov	ax,#0x7000
 	xor	bx,bx
 	int	0x10
@@ -662,15 +882,32 @@
 	jne	noevrx
 	shr	dx,#4
 	cmp	dx,#0x678
-	je	istrid
+	je	evtrid
 	cmp	dx,#0x236
-	je	istrid
-	lea	si,dsceverex
-	lea	di,moeverex
-	br	selmod
-istrid:	lea	cx,ev2tri
-	jmp	cx
-noevrx:	lea	si,idgenoa		! Check Genoa 'clues'
+	jne	evrxok
+evtrid:	lea	bp,trident_md
+evrxok:	ret
+
+noevrx:	xor	bp,bp
+	ret
+
+everex_md:
+	.byte	0x03, 0x22, 0x50
+	.byte	0x04, 0x3c, 0x50
+	.byte	0x07, 0x2b, 0x64
+	.byte	0x08, 0x4b, 0x64
+	.byte	0x0a, 0x19, 0x84
+	.byte	0x0b, 0x2c, 0x84
+	.byte	0x16, 0x1e, 0x50
+	.byte	0x18, 0x1b, 0x64
+	.byte	0x21, 0x40, 0xa0
+	.byte	0x40, 0x1e, 0x84
+	.byte	0
+
+! Genoa.
+
+genoa_test:
+	lea	si,idgenoa		! Check Genoa 'clues'
 	xor 	ax,ax
 	seg es
 	mov	al,[0x37]
@@ -686,32 +923,72 @@
 	seg es
 	cmp	al,(di)
 l2:	loope 	l1
-	cmp	cx,#0x00
-	jne	nogen
-	lea	si,dscgenoa
-	lea	di,mogenoa
-	br	selmod
-nogen:	cld
+	or	cx,cx
+	je	isgen
+	xor	bp,bp
+isgen:	ret
+
+idgenoa: .byte	0x77, 0x00, 0x99, 0x66
+
+genoa_md:
+	.byte	0x58, 0x20, 0x50
+	.byte	0x5a, 0x2a, 0x64
+	.byte	0x60, 0x19, 0x84
+	.byte	0x61, 0x1d, 0x84
+	.byte	0x62, 0x20, 0x84
+	.byte	0x63, 0x2c, 0x84
+	.byte	0x64, 0x3c, 0x84
+	.byte	0x6b, 0x4f, 0x64
+	.byte	0x72, 0x3c, 0x50
+	.byte	0x74, 0x42, 0x50
+	.byte	0x78, 0x4b, 0x64
+	.byte	0
+
+! OAK
+
+oak_test:
 	lea	si,idoakvga
 	mov	di,#0x08
 	mov	cx,#0x08
 	repe
 	cmpsb
-	jne	nooak
-	lea	si,dscoakvga
-	lea	di,mooakvga
-	br	selmod
-nooak:	cld
-	lea	si,idparadise		! Check Paradise 'clues'
+	je	isoak
+	xor	bp,bp
+isoak:	ret
+
+idoakvga: .ascii  "OAK VGA "
+
+oak_md: .byte	0x4e, 0x3c, 0x50
+	.byte	0x4f, 0x3c, 0x84
+	.byte	0x50, 0x19, 0x84
+	.byte	0x51, 0x2b, 0x84
+	.byte	0
+
+! WD Paradise.
+
+paradise_test:
+	lea	si,idparadise
 	mov	di,#0x7d
 	mov	cx,#0x04
 	repe
 	cmpsb
-	jne	nopara
-	lea	si,dscparadise
-	lea	di,moparadise
-	br	selmod
-nopara:	mov	dx,#0x3c4		! Check Trident 'clues'
+	je	ispara
+	xor	bp,bp
+ispara:	ret
+
+idparadise:	.ascii	"VGA="
+
+paradise_md:
+	.byte	0x41, 0x22, 0x50
+	.byte	0x47, 0x1c, 0x84
+	.byte	0x55, 0x19, 0x84
+	.byte	0x54, 0x2c, 0x84
+	.byte	0
+
+! Trident.
+
+trident_test:
+	mov	dx,#0x3c4
 	mov	al,#0x0e
 	out	dx,al
 	inc	dx
@@ -730,11 +1007,24 @@
 clrb2:	out	dx,al
 	and	ah,#0x0f
 	cmp	ah,#0x02
-	jne	notrid
-ev2tri:	lea	si,dsctrident
-	lea	di,motrident
-	jmp	selmod
-notrid:	mov	dx,#0x3cd		! Check Tseng 'clues'
+	je	istrid
+	xor	bp,bp
+istrid:	ret
+
+trident_md:
+	.byte	0x50, 0x1e, 0x50
+	.byte	0x51, 0x2b, 0x50
+	.byte	0x52, 0x3c, 0x50
+	.byte	0x57, 0x19, 0x84
+	.byte	0x58, 0x1e, 0x84
+	.byte	0x59, 0x2b, 0x84
+	.byte	0x5a, 0x3c, 0x84
+	.byte	0
+
+! Tseng.
+
+tseng_test:
+	mov	dx,#0x3cd
 	in	al,dx			! Could things be this simple ! :-)
 	mov	bl,al
 	mov	al,#0x55
@@ -744,11 +1034,22 @@
 	mov	al,bl
 	out	dx,al
 	cmp	ah,#0x55
- 	jne	notsen
-	lea	si,dsctseng
-	lea	di,motseng
-	jmp	selmod
-notsen:	mov	dx,#0x3cc		! Check Video7 'clues'
+ 	je	istsen
+	xor	bp,bp
+istsen:	ret
+
+tseng_md:
+	.byte	0x26, 0x3c, 0x50
+	.byte	0x2a, 0x28, 0x64
+	.byte	0x23, 0x19, 0x84
+	.byte	0x24, 0x1c, 0x84
+	.byte	0x22, 0x2c, 0x84
+	.byte	0
+
+! Video7.
+
+video7_test:
+	mov	dx,#0x3cc
 	in	al,dx
 	mov	dx,#0x3b4
 	and	al,#0x01
@@ -777,115 +1078,78 @@
 	mov	al,#0x55
 	xor	al,#0xea
 	cmp	al,bh
-	jne	novid7
-	lea	si,dscvideo7
-	lea	di,movideo7
-	jmp	selmod
-novid7:	lea	si,dsunknown
-	lea	di,mounknown
-selmod:	xor	cx,cx
-	mov	cl,(di)
-	mov	ax,modesave
-	cmp	ax,#ASK_VGA
-	je	askmod
-	cmp	ax,#NORMAL_VGA
-	je	askmod
-	cmp	al,cl
-	jl	gotmode
-	push	si
-	lea	si,msg4
+	je	isvid7
+	xor	bp,bp
+isvid7:	ret
+
+video7_md:
+	.byte	0x40, 0x2b, 0x50
+	.byte	0x43, 0x3c, 0x50
+	.byte	0x44, 0x3c, 0x64
+	.byte	0x41, 0x19, 0x84
+	.byte	0x42, 0x2c, 0x84
+	.byte	0x45, 0x1c, 0x84
+	.byte	0
+
+!
+! Displaying of the mode list.
+!
+
+listmodes:
+	lea	si,listhdr
 	call	prtstr
-	pop	si
-askmod:	push	si
-	lea	si,msg2
+	lea	bx,modelist
+	mov	cl,#0x30
+listm1:	mov	modenr,cl
+	lea	si,modestring
 	call	prtstr
-	pop	si
-	push	si
-	push	cx
-tbl:	pop	bx
-	push	bx
-	mov	al,bl
-	sub	al,cl
-	call	modepr
-	lodsw
-	xchg	al,ah
+	mov	al,(bx+3)
 	call	dprnt
-	xchg	ah,al
-	push	ax
 	mov	al,#0x78
 	call	prnt1
-	pop	ax
+	mov	al,(bx+2)
 	call	dprnt
-	push	si
-	lea	si,crlf		! print CR+LF
-	call	prtstr
-	pop	si
-	loop	tbl
-	pop	cx
-	lea	si,msg3
+#ifdef SHOW_BIOS_MODES
+	mov	al,#0x20
+	call	prnt1
+	mov	al,#0x28
+	call	prnt1
+	mov	al,(bx+1)
+	call	prthex
+	mov	al,(bx)
+	call	prthex
+	mov	al,#0x29
+	call	prnt1
+#endif
+	lea	si,crlf
 	call	prtstr
-	pop	si
-	add	cl,#0x30
-	jmp	nonum
-nonumb:	call	beep
-nonum:	call	getkey
-	cmp	al,#0x30	! ascii `0'
-	jb	nonumb
-	cmp	al,#0x3a	! ascii `9'
-	jbe	number
-	cmp	al,#0x61	! ascii `a'
-	jb	nonumb
-	cmp	al,#0x7a	! ascii `z'
-	ja	nonumb
-	sub	al,#0x27
-	cmp	al,cl
-	jae	nonumb
-	sub	al,#0x30
-	jmp	gotmode
-number: cmp	al,cl
-	jae	nonumb
-	sub	al,#0x30
-gotmode:	xor	ah,ah
-	or	al,al
-	beq	vga50
-	push	ax
-	dec	ax
-	beq	vga28
-	add	di,ax
-	mov	al,(di)
-	int 	0x10
-	pop	ax
-	shl	ax,#1
-	add	si,ax
-	lodsw
-	pop	ds
-	ret
+	add	bx,#4
+	inc	cl
+	cmp	cl,#0x3a
+	jnz	listm2
+	mov	cl,#0x61
+listm2:	cmp	bx,di
+	jc	listm1
+	lea	si,prompt
+	br	prtstr
 
-! Routine to write al into a VGA-register that is
-! accessed via an index register
-!
-! dx contains the address of the index register
-! al contains the index
-! ah contains the value to write to the data register (dx + 1)
-!
-! no registers are changed
+! Routine to print a hexadecimal byte (AL) on screen.
 
-outidx:	out	dx,al
-	push	ax
-	mov	al,ah
-	inc	dx
-	out	dx,al
-	dec	dx
+#ifdef SHOW_BIOS_MODES
+prthex:	push	ax
+	shr	al,#4
+	call	prth1
 	pop	ax
-	ret
-inidx:	out	dx,al
-	inc	dx
-	in	al,dx
-	dec	dx
-	ret
+prth1:	and	al,#15
+	cmp	al,#10
+	jc	prth2
+	add	al,#7
+prth2:	add	al,#0x30
+	br	prnt1
+#endif
 
 ! Routine to print a decimal value on screen, the value to be
-! printed is put in al (i.e 0-255). 
+! printed is put in AL (i.e 0-255). 
 
 dprnt:	push	ax
 	push	cx
@@ -906,25 +1170,65 @@
 	ret
 
 !
-! Routine to print the mode number key on screen. Mode numbers
-! 0-9 print the ascii values `0' to '9', 10-35 are represented by
-! the letters `a' to `z'. This routine prints some spaces around the
-! mode no.
+! Read a key and return the (US-)ascii code in al, scan code in ah
 !
+getkey:
+	xor	ah,ah
+	int	0x16
+	ret
 
-modepr:	push	ax
-	cmp	al,#0x0a
-	jb	digit		! Here is no check for number > 35
-	add	al,#0x27
-digit:	add	al,#0x30
-	mov	modenr, al
-	push 	si
-	lea	si, modestring
-	call	prtstr
-	pop	si
-	pop	ax
+!
+! Read a key with a timeout of 30 seconds. The cmos clock is used to get
+! the time.
+!
+getkt:
+	call	gettime
+	add	al,#30		! wait 30 seconds
+	cmp	al,#60
+	jl	lminute
+	sub	al,#60
+lminute:
+	mov	cl,al
+again:	mov	ah,#0x01
+	int	0x16
+	jnz	getkey		! key pressed, so get it
+	call	gettime
+	cmp	al,cl
+	jne	again
+	mov	al,#0x20	! timeout, return default char `space'
 	ret
 
+!
+! Flush the keyboard buffer
+!
+flush:	mov	ah,#0x01
+	int	0x16
+	jz	empty
+	xor	ah,ah
+	int	0x16
+	jmp	flush
+empty:	ret
+
+!
+! Read the cmos clock. Return the seconds in al
+!
+gettime:
+	push	cx
+	mov	ah,#0x02
+	int	0x1a
+	mov	al,dh			! dh contains the seconds
+	and	al,#0x0f
+	mov	ah,dh
+	mov	cl,#0x04
+	shr	ah,cl
+	aad
+	pop	cx
+	ret
+
+!
+! Descriptor table for our protected mode transition.
+!
+
 gdt:
 	.word	0,0,0,0		! dummy
 
@@ -948,68 +1252,36 @@
 	.word	0x800		! gdt limit=2048, 256 GDT entries
 	.word	512+gdt,0x9	! gdt base = 0X9xxxx
 
-msg1:		.ascii	"Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
-		db	0x0d, 0x0a, 0x0a, 0x00
-msg2:		.ascii	"Mode:  COLSxROWS:"
+!
+! Assorted messages.
+!
+
+keymsg:		.ascii	"Press <RETURN> to see video modes available, <SPACE> to continue or wait 30 secs"
+		db	0x0d, 0x0a, 0x00
+listhdr:	.ascii	"Mode:  COLSxROWS:"
 		db	0x0d, 0x0a, 0x0a, 0x00
-msg3:		db	0x0d, 0x0a
+prompt:		db	0x0d, 0x0a
 		.ascii	"Choose mode by pressing the corresponding number or letter."
 crlf:		db	0x0d, 0x0a, 0x00
-msg4:		.ascii	"You passed an undefined mode number to setup. Please choose a new mode."
+undefd:		.ascii	"You passed an undefined mode number to setup. Please choose a new mode."
 		db	0x0d, 0x0a, 0x0a, 0x07, 0x00
 modestring:	.ascii	"   "
 modenr:		db	0x00	! mode number
 		.ascii	":    "
 		db	0x00
-		
-idati:		.ascii	"761295520"
-idcandt:	.byte	0xa5
-idgenoa:	.byte	0x77, 0x00, 0x99, 0x66
-idparadise:	.ascii	"VGA="
-idoakvga:	.ascii  "OAK VGA "
-idS3:		.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
-		.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
-
-! Manufacturer:	  Numofmodes+2:	Mode:
-! Number of modes is the number of chip-specific svga modes plus the extended
-! modes available on any vga (currently 2)
-
-moati:		.byte	0x06,	0x23, 0x33, 0x22, 0x21
-moahead:	.byte	0x07,	0x22, 0x23, 0x24, 0x2f, 0x34
-mocandt:	.byte	0x04,	0x60, 0x61
-mocirrus:	.byte	0x06,	0x1f, 0x20, 0x22, 0x31
-moeverex:	.byte	0x0c,	0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
-mogenoa:	.byte	0x0c,	0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
-moparadise:	.byte	0x04,	0x55, 0x54
-motrident:	.byte	0x09,	0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
-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
-mo_S3:		.byte	0x04,	0x54, 0x55
-mounknown:	.byte	0x02
-
-!			msb = Cols lsb = Rows:
-! The first two modes are standard vga modes available on any vga.
-! mode 0 is 80x50 and mode 1 is 80x28
-
-dscati:		.word	0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419
-dscahead:	.word	0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
-dsccandt:	.word	0x5032, 0x501c, 0x8419, 0x8432
-dsccirrus:	.word	0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
-dsceverex:	.word	0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
-dscgenoa:	.word	0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
-dscparadise:	.word	0x5032, 0x501c, 0x8419, 0x842c
-dsctrident:	.word 	0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
-dsctseng:	.word	0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
-dscvideo7:	.word	0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
-dscoakvga:	.word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
-dsc_S3:		.word	0x5032, 0x501c, 0x842b, 0x8419
-dsunknown:	.word	0x5032, 0x501c
-modesave:	.word	SVGA_MODE
+
+modesave:	.word	0	! Requsted mode ID.
 
 ! This must be last
 setup_sig1:	.word	SIG1
 setup_sig2:	.word	SIG2
+
+! After our code and data, we'll store the mode list.
+! Mode record:	.word	modenr
+!		.byte	lines
+!		.byte	columns
+! Mode numbers used: 0=current, >=0x100=VESA, -1=80x50, -2=80x28
+modelist:
 
 .text
 endtext:
diff -u --recursive --new-file v1.3.41/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.41/linux/arch/i386/defconfig	Thu Nov  9 11:23:46 1995
+++ linux/arch/i386/defconfig	Thu Nov 16 08:38:57 1995
@@ -49,7 +49,7 @@
 #
 # CONFIG_INET_PCTCP is not set
 # CONFIG_INET_RARP is not set
-CONFIG_INET_SNARL=y
+# CONFIG_NO_PATH_MTU_DISCOVERY is not set
 # CONFIG_TCP_NAGLE_OFF is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
@@ -102,6 +102,7 @@
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
diff -u --recursive --new-file v1.3.41/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v1.3.41/linux/arch/i386/kernel/Makefile	Wed Nov  8 07:11:29 1995
+++ linux/arch/i386/kernel/Makefile	Tue Nov 14 16:03:52 1995
@@ -14,8 +14,15 @@
 
 #.S.s:
 #	$(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+
+ifdef SMP
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $< -o $*.o
+else
 .S.o:
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+endif
 
 all: kernel.o head.o
 
@@ -24,12 +31,19 @@
             ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o
 
 ifdef SMP
+
 O_OBJS += smp.o
-endif
+
+head.o: head.S $(TOPDIR)/include/linux/tasks.h
+	$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o
+
+else
 
 head.o: head.S $(TOPDIR)/include/linux/tasks.h
 	$(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o
 
+endif
+
 hexify:
 	$(HOSTCC) hexify.c -o hexify
 
@@ -48,10 +62,10 @@
 	$(AS386) -o $@ $<
 
 trampoline.s: trampoline.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
-	$(CPP) -traditional $< -o $@
+	$(CPP) -D__SMP__ -traditional $< -o $@
 
 trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
-	$(CPP) -traditional $< -o $@
+	$(CPP) -D__SMP__ -traditional $< -o $@
 
 clean:
 	rm -f trampoline hexify
diff -u --recursive --new-file v1.3.41/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v1.3.41/linux/arch/i386/kernel/bios32.c	Sat Sep  9 15:26:51 1995
+++ linux/arch/i386/kernel/bios32.c	Tue Nov 14 07:35:31 1995
@@ -383,6 +383,12 @@
 		case PCIBIOS_BAD_REGISTER_NUMBER:
 			return "BAD_REGISTER_NUMBER";
 
+                case PCIBIOS_SET_FAILED:          
+			return "SET_FAILED";
+
+                case PCIBIOS_BUFFER_TOO_SMALL:    
+			return "BUFFER_TOO_SMALL";
+
 		default:
 			sprintf (buf, "UNKNOWN RETURN 0x%x", error);
 			return buf;
diff -u --recursive --new-file v1.3.41/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v1.3.41/linux/arch/i386/kernel/setup.c	Wed Nov  8 07:11:30 1995
+++ linux/arch/i386/kernel/setup.c	Tue Nov 14 07:37:42 1995
@@ -103,8 +103,8 @@
 	if (memory_end > 16*1024*1024)
 		memory_end = 16*1024*1024;
 #endif
-	if (MOUNT_ROOT_RDONLY)
-		root_mountflags |= MS_RDONLY;
+	if (!MOUNT_ROOT_RDONLY)
+		root_mountflags &= ~MS_RDONLY;
 	memory_start = (unsigned long) &_end;
 	init_task.mm->start_code = TASK_SIZE;
 	init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
diff -u --recursive --new-file v1.3.41/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.41/linux/drivers/block/floppy.c	Mon Nov 13 12:36:43 1995
+++ linux/drivers/block/floppy.c	Thu Nov 16 08:05:10 1995
@@ -2440,14 +2440,21 @@
 		raw_cmd->flags |= FD_RAW_READ;
 		COMMAND = FM_MODE(floppy,FD_READ);
 	} else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
+		unsigned long dma_limit;
 		int direct, indirect;
 
 		indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
 			sector_t;
 
-		max_size = minimum(buffer_chain_size(),
-				   (MAX_DMA_ADDRESS - 
-				    ((unsigned long) CURRENT->buffer))>>9);
+		/*
+		 * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+		 * on a 64 bit machine!
+		 */
+		max_size = buffer_chain_size();
+		dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
+		if ((unsigned long) max_size > dma_limit) {
+			max_size = dma_limit;
+		}
 		/* 64 kb boundaries */
 		if (CROSS_64KB(CURRENT->buffer, max_size << 9))
 			max_size = (K_64 - ((long) CURRENT->buffer) % K_64)>>9;
@@ -3033,8 +3040,7 @@
 			return -EPERM;
 		LOCK_FDC(drive,1);
 		for (cnt = 0; cnt < N_DRIVE; cnt++){
-			if (TYPE(drive_state[cnt].fd_device) ==
-			    type &&
+			if (TYPE(drive_state[cnt].fd_device) == type &&
 			    drive_state[cnt].fd_ref)
 				set_bit(drive, &fake_change);
 		}
@@ -3047,7 +3053,9 @@
 		for (cnt = 0; cnt < N_DRIVE; cnt++){
 			if (TYPE(drive_state[cnt].fd_device) == type &&
 			    drive_state[cnt].fd_ref)
-				check_disk_change(drive_state[cnt].fd_device);
+				check_disk_change(
+					MKDEV(FLOPPY_MAJOR,
+					      drive_state[cnt].fd_device));
 		}
 	} else {
 		LOCK_FDC(drive,1);
@@ -3125,7 +3133,7 @@
 				DPRINT1("warning: obsolete ioctl 0x%x\n",ocmd);
 				DPRINT("please recompile your program\n");
 				/* these ioctls only existed
-				 * in two (development)
+				 * in six (development)
 				 * kernels anyways. That's why we
 				 * complain about these, and not about
 				 * the much older 0x00xx ioctl's
@@ -3206,12 +3214,12 @@
 		return -EPERM;
 
 	/* verify writability of result, and fail early */
-	if (_IOC_DIR(cmd) & _IOC_WRITE)
+	if (_IOC_DIR(cmd) & _IOC_READ)
 		ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
 		
 	/* copyin */
 	CLEARSTRUCT(&inparam);
-	if (_IOC_DIR(cmd) & _IOC_READ)
+	if (_IOC_DIR(cmd) & _IOC_WRITE)
 		ECALL(fd_copyin((void *)param, &inparam, size))
 
 	switch (cmd) {
@@ -3304,7 +3312,7 @@
 			return -EINVAL;
 	}
 
-	if (_IOC_DIR(cmd) & _IOC_WRITE)
+	if (_IOC_DIR(cmd) & _IOC_READ)
 		return fd_copyout((void *)param, outparam, size);
 	else
 		return 0;
@@ -3479,11 +3487,10 @@
 	}
 
 	UDRS->fd_device = MINOR(inode->i_rdev);
-
-	if (old_dev && old_dev != inode->i_rdev) {
+	if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
 		if (buffer_drive == drive)
 			buffer_track = -1;
-		invalidate_buffers(old_dev);
+		invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
 	}
 
 	/* Allow ioctls if we have write-permissions even if read-only open */
@@ -3831,6 +3838,7 @@
 		CLEARSTRUCT(UDRS);
 		CLEARSTRUCT(UDRWE);
 		UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+		UDRS->fd_device = -1;
 		floppy_track_buffer = NULL;
 		max_buffer_sectors = 0;
 	}
diff -u --recursive --new-file v1.3.41/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.41/linux/drivers/block/ll_rw_blk.c	Mon Oct 23 18:02:03 1995
+++ linux/drivers/block/ll_rw_blk.c	Tue Nov 14 08:41:32 1995
@@ -104,7 +104,7 @@
  * force the transfer to start only after we have put all the requests
  * on the list.
  */
-static void plug_device(struct blk_dev_struct * dev, struct request * plug)
+static inline void plug_device(struct blk_dev_struct * dev, struct request * plug)
 {
 	unsigned long flags;
 
@@ -121,7 +121,7 @@
 /*
  * remove the plug and let it rip..
  */
-static void unplug_device(struct blk_dev_struct * dev)
+static inline void unplug_device(struct blk_dev_struct * dev)
 {
 	struct request * req;
 	unsigned long flags;
diff -u --recursive --new-file v1.3.41/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v1.3.41/linux/drivers/cdrom/mcd.c	Mon Nov 13 12:36:43 1995
+++ linux/drivers/cdrom/mcd.c	Wed Nov 15 08:11:14 1995
@@ -62,22 +62,11 @@
 
 */
 
-#include <linux/config.h>
 
-#ifdef MODULE
-# include <linux/module.h>
-#endif
-
-#include <linux/version.h>
+#include <linux/module.h>
 
 #ifdef MODULE
-# ifndef CONFIG_MODVERSIONS
-    char kernel_version[]= UTS_RELEASE;
-# endif
 #define mcd_init init_module
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
 #endif
 
 #include <linux/errno.h>
diff -u --recursive --new-file v1.3.41/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v1.3.41/linux/drivers/cdrom/mcdx.c	Mon Nov 13 12:36:43 1995
+++ linux/drivers/cdrom/mcdx.c	Wed Nov 15 08:12:14 1995
@@ -37,23 +37,7 @@
 		= "mcdx.c,v 1.17 1995/11/06 01:07:57 heiko Exp";
 #endif
 
-#include <linux/config.h>
-
-#ifdef MODULE
 #include <linux/module.h>
-#endif
-
-#include <linux/version.h>
-
-#ifdef MODULE
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[] = UTS_RELEASE;
-#endif
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#define MOD_IN_USE 1
-#endif MODULE
 
 #include <linux/errno.h>
 #include <linux/signal.h>
diff -u --recursive --new-file v1.3.41/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v1.3.41/linux/drivers/char/cyclades.c	Mon Nov 13 12:36:43 1995
+++ linux/drivers/char/cyclades.c	Tue Nov 14 07:43:33 1995
@@ -1,5 +1,5 @@
 static char rcsid[] =
-"$Revision: 1.36.3.2 $$Date: 1995/09/08 22:07:14 $";
+"$Revision: 1.36.3.4 $$Date: 1995/11/13 20:45:10 $";
 /*
  *  linux/drivers/char/cyclades.c
  *
@@ -24,6 +24,19 @@
  *   int cy_open(struct tty_struct *tty, struct file *filp);
  *
  * $Log: cyclades.c,v $
+ * Revision 1.36.3.4  1995/11/13  20:45:10  bentson
+ * Changes by Corey Minyard <minyard@wf-rch.cirr.com> distributed
+ * in 1.3.41 kernel to remove a possible race condition, extend
+ * some error messages, and let the driver run as a loadable module
+ * Change by Alan Wendt <alan@ez0.ezlink.com> to remove a
+ * possible race condition.
+ * Change by Marcio Saito <marcio@cyclades.com> to fix PCI addressing.
+ *
+ * Revision 1.36.3.3  1995/11/13  19:44:48  bentson
+ * Changes by Linus Torvalds in 1.3.33 kernel distribution
+ * required due to reordering of driver initialization.
+ * Drivers are now initialized *after* memory management.
+ *
  * Revision 1.36.3.2  1995/09/08  22:07:14  bentson
  * remove printk from ISR; fix typo
  *
@@ -452,12 +465,13 @@
         console_print(data);
     restore_flags(flags);
 }
-char scrn[2];
 void
 CP(char data){
   unsigned long flags;
+  char scrn[2];
     save_flags(flags); cli();
         scrn[0] = data;
+        scrn[1] = '\0';
         console_print(scrn);
     restore_flags(flags);
 }/* CP */
@@ -1296,8 +1310,10 @@
      */
     save_flags(flags); cli();
 	if (info->xmit_buf){
-	    free_page((unsigned long) info->xmit_buf);
-	    info->xmit_buf = 0;
+            unsigned long temp;
+            temp = info->xmit_buf;
+            info->xmit_buf = 0;
+	    free_page((unsigned long) temp);
 	}
 
 	base_addr[CyCAR<<index] = (u_char)channel;
@@ -2693,7 +2709,7 @@
            and this must be a Cyclom-16Y, not a Cyclom-32Ye.
         */
         if (chip_number == 4
-        && *(true_base_addr + cy_chip_offset[0] + CyGFRCR) == 0){
+        && *(true_base_addr + (cy_chip_offset[0]<<index) + (CyGFRCR<<index)) == 0){
 	    return chip_number;
         }
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v1.3.41/linux/drivers/net/3c503.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/3c503.c	Tue Nov 14 07:48:52 1995
@@ -25,6 +25,7 @@
     Changelog:
 
     Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards.
+    Paul Gortmaker	: multiple card support for module users.
 
 */
 
@@ -182,16 +183,24 @@
 	return ENODEV;
     }
 
-    if (dev == NULL)
-	dev = init_etherdev(0, sizeof(struct ei_device));
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+	printk("3c503.c: Passed a NULL device.\n");
+	dev = init_etherdev(0, 0);
+    }
 
     if (ei_debug  &&  version_printed++ == 0)
 	printk(version);
 
     dev->base_addr = ioaddr;
-    ethdev_init(dev);
 
-    printk("%s: 3c503 at i/o base %#3x, node address", dev->name, ioaddr);
+    /* Allocate dev->priv and fill in 8390 specific dev fields. */
+    if (ethdev_init(dev)) {
+	printk ("3c503: unable to allocate memory for dev->priv.\n");
+	return -ENOMEM;
+     }
+
+    printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
 
     /* Retrieve and print the ethernet address. */
     for (i = 0; i < 6; i++)
@@ -220,6 +229,7 @@
 #else
     ei_status.interface_num = dev->mem_end & 0xf;
 #endif
+    printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
 
     if ((membase_reg & 0xf0) == 0) {
 	dev->mem_start = 0;
@@ -241,7 +251,7 @@
 		writel(test_val, mem_base + i);
 		if (readl(mem_base) != 0xba5eba5e
 		    || readl(mem_base + i) != test_val) {
-		    printk(" memory failure or memory address conflict.\n");
+		    printk("3c503.c: memory failure or memory address conflict.\n");
 		    dev->mem_start = 0;
 		    ei_status.name = "3c503-PIO";
 		    break;
@@ -292,7 +302,7 @@
     if (dev->irq == 2)
 	dev->irq = 9;
     else if (dev->irq > 5 && dev->irq != 9) {
-	printk("\n3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+	printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
 	       dev->irq);
 	dev->irq = 0;
     }
@@ -304,7 +314,7 @@
     dev->stop = &el2_close;
 
     if (dev->mem_start)
-	printk("\n%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
 		dev->name, ei_status.name, (wordlength+1)<<3,
 		dev->mem_start, dev->mem_end-1);
 
@@ -344,8 +354,11 @@
 	    return -EAGAIN;
 	}
     }
+
     el2_init_card(dev);
-    return ei_open(dev);
+    ei_open(dev);
+    MOD_INC_USE_COUNT;
+    return 0;
 }
 
 static int
@@ -356,9 +369,8 @@
     irq2dev_map[dev->irq] = NULL;
     outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
 
-    NS8390_init(dev, 0);
-    dev->start = 0;
-
+    ei_close(dev);
+    MOD_DEC_USE_COUNT;
     return 0;
 }
 
@@ -526,56 +538,69 @@
     }
     outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
 }
+
+
 #ifdef MODULE
-static struct device el2_drv =
-{"3c503", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_probe };
-       
-static struct device el2pio_drv =
-{"3c503pio", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_pio_probe };
-
-static int io = 0x300;
-static int irq = 0;
-
-static int no_pio = 1;
-int init_module(void)
-{
-	int rc1, rc2;
-	el2_drv.base_addr = io;
-	el2_drv.irq       = irq;
-	el2pio_drv.base_addr = io;
-	el2pio_drv.irq       = irq;
-
-	if (io == 0)
-	    printk("3c503: You should not use auto-probing with insmod!\n");
-
-	rc2 = 0;
-	no_pio = 1;
-	rc1 = register_netdev(&el2_drv);
-	if (rc1 != 0) {
-	    rc2 = register_netdev(&el2pio_drv);
-	    no_pio = 0;
+#define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, };
+static struct device dev_el2[MAX_EL2_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_EL2_CARDS] = { 0, };
+static int irq[MAX_EL2_CARDS]  = { 0, };
+static int xcvr[MAX_EL2_CARDS] = { 0, };	/* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+		struct device *dev = &dev_el2[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */
+		dev->init = el2_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
 
-	if (rc1 != 0 && rc2 != 0)
-		return -EIO;
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	int ioaddr;
+	int this_dev;
 
-	if (no_pio) {
-		ioaddr = el2_drv.base_addr;
-		unregister_netdev(&el2_drv);
-	} else {
-		ioaddr = el2pio_drv.base_addr;
-		unregister_netdev(&el2pio_drv);
+	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+		struct device *dev = &dev_el2[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: el2_close() handles free_irq + irq2dev map */
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(dev->base_addr, EL2_IO_EXTENT);
+			unregister_netdev(dev);
+		}
 	}
-
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(ioaddr, EL2_IO_EXTENT);
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v1.3.41/linux/drivers/net/8390.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/8390.c	Tue Nov 14 07:48:52 1995
@@ -55,7 +55,6 @@
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/skbuff.h>
 
 #include "8390.h"
 
@@ -118,10 +117,11 @@
 int ei_open(struct device *dev)
 {
     struct ei_device *ei_local = (struct ei_device *) dev->priv;
-    
-    if ( ! ei_local) {
-		printk("%s: Opening a non-existent physical device\n", dev->name);
-		return ENXIO;
+
+    /* This can't happen unless somebody forgot to call ethdev_init(). */
+    if (ei_local == NULL) {
+	printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
+	return -ENXIO;
     }
     
     irq2dev_map[dev->irq] = dev;
@@ -131,6 +131,14 @@
     return 0;
 }
 
+/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+int ei_close(struct device *dev)
+{
+    NS8390_init(dev, 0);
+    dev->start = 0;
+    return 0;
+}
+
 static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
 {
     int e8390_base = dev->base_addr;
@@ -603,10 +611,6 @@
 		ei_local->pingpong = ei_pingpong;
     }
     
-    /* The open call may be overridden by the card-specific code. */
-    if (dev->open == NULL)
-		dev->open = &ei_open;
-    /* We should have a dev->stop entry also. */
     dev->hard_start_xmit = &ei_start_xmit;
     dev->get_stats	= get_stats;
 #ifdef HAVE_MULTICAST
diff -u --recursive --new-file v1.3.41/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v1.3.41/linux/drivers/net/8390.h	Wed Oct  4 14:14:30 1995
+++ linux/drivers/net/8390.h	Thu Nov 16 09:20:47 1995
@@ -32,12 +32,13 @@
 extern int ethdev_init(struct device *dev);
 extern void NS8390_init(struct device *dev, int startp);
 extern int ei_open(struct device *dev);
+extern int ei_close(struct device *dev);
 extern void ei_interrupt(int irq, struct pt_regs *regs);
 
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c */
 extern struct device *irq2dev_map[16];
-extern void autoirq_setup(int waittime);
+extern int autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 #endif
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.41/linux/drivers/net/Config.in	Sat Nov 11 17:41:32 1995
+++ linux/drivers/net/Config.in	Tue Nov 14 16:03:52 1995
@@ -10,11 +10,7 @@
 if [ ! "$CONFIG_PPP" = "n" ]; then
   comment 'CCP compressors for PPP are only built as modules.'
 fi
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
-else
-  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
-fi
+bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 tristate 'PLIP (parallel port) support' CONFIG_PLIP
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA
diff -u --recursive --new-file v1.3.41/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.41/linux/drivers/net/Makefile	Sat Nov 11 17:41:32 1995
+++ linux/drivers/net/Makefile	Tue Nov 14 07:34:46 1995
@@ -139,7 +139,7 @@
   endif
 endif
 
-ifneq ($(CONFIG_PPP),n)
+ifdef CONFIG_PPP
   M_OBJS += bsd_comp.o
 endif
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.41/linux/drivers/net/Space.c	Tue Oct 10 18:46:35 1995
+++ linux/drivers/net/Space.c	Tue Nov 14 07:48:52 1995
@@ -97,15 +97,21 @@
 #if defined(CONFIG_EL2) || defined(EL2)	/* 3c503 */
 	&& el2_probe(dev)
 #endif
-#if defined(CONFIG_NE2000) || defined(NE2000)
-	&& ne_probe(dev)
-#endif
 #if defined(CONFIG_HPLAN) || defined(HPLAN)
 	&& hp_probe(dev)
 #endif
 #if defined(CONFIG_HPLAN_PLUS)
 	&& hp_plus_probe(dev)
 #endif
+#ifdef CONFIG_AC3200		/* Ansel Communications EISA 3200. */
+	&& ac3200_probe(dev)
+#endif
+#ifdef CONFIG_E2100		/* Cabletron E21xx series. */
+	&& e2100_probe(dev)
+#endif
+#if defined(CONFIG_NE2000) || defined(NE2000)
+	&& ne_probe(dev)
+#endif
 #ifdef CONFIG_AT1500
 	&& at1500_probe(dev)
 #endif
@@ -147,12 +153,6 @@
 #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
 #ifdef CONFIG_DE600		/* D-Link DE-600 adapter */
 	&& de600_probe(dev)
diff -u --recursive --new-file v1.3.41/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c
--- v1.3.41/linux/drivers/net/ac3200.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/ac3200.c	Tue Nov 14 07:48:52 1995
@@ -97,13 +97,16 @@
 	else if (ioaddr > 0)		/* Don't probe at all. */
 		return ENXIO;
 
-	/* If you have a pre-pl15 machine you should delete this line. */
+	/* If you have a pre 0.99pl15 machine you should delete this line. */
 	if ( ! EISA_bus)
 		return ENXIO;
 
-	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000)
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (check_region(ioaddr, AC_IO_EXTENT))
+			continue;
 		if (ac_probe1(ioaddr, dev) == 0)
 			return 0;
+	}
 
 	return ENODEV;
 }
@@ -138,6 +141,13 @@
 			return ENODEV;
 		}
 
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("ac3200.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
 	for(i = 0; i < ETHER_ADDR_LEN; i++)
 		dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
 
@@ -159,6 +169,13 @@
 		return EAGAIN;
 	}
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to allocate memory for dev->priv.\n");
+		free_irq(dev->irq);
+		return -ENOMEM;
+	}
+
 	request_region(ioaddr, AC_IO_EXTENT, "ac3200");
 
 	dev->base_addr = ioaddr;
@@ -180,8 +197,6 @@
 	dev->mem_end = dev->rmem_end = dev->mem_start
 		+ (AC_STOP_PG - AC_START_PG)*256;
 
-	ethdev_init(dev);
-
 	ei_status.name = "AC3200";
 	ei_status.tx_start_page = AC_START_PG;
 	ei_status.rx_start_page = AC_START_PG + TX_PAGES;
@@ -208,7 +223,6 @@
 
 static int ac_open(struct device *dev)
 {
-	int rc;
 #ifdef notyet
 	/* Someday we may enable the IRQ and shared memory here. */
 	int ioaddr = dev->base_addr;
@@ -217,8 +231,7 @@
 		return -EAGAIN;
 #endif
 
-	rc = ei_open(dev);
-	if (rc != 0) return rc;
+	ei_open(dev);
 
 	MOD_INC_USE_COUNT;
 
@@ -293,7 +306,7 @@
 	irq2dev_map[dev->irq] = 0;
 #endif
 
-	NS8390_init(dev, 0);
+	ei_close(dev);
 
 	MOD_DEC_USE_COUNT;
 
@@ -301,35 +314,64 @@
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ac3200 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, ac3200_probe };
-
-static int io = 0;
-static int irq = 0;
-
-int init_module(void)
-{
-	dev_ac3200.base_addr = io;
-	dev_ac3200.irq       = irq;
-	if (register_netdev(&dev_ac3200) != 0) {
-		printk("ac3200: register_netdev() returned non-zero.\n");
-		return -EIO;
+#define MAX_AC32_CARDS	4	/* Max number of AC32 cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, };
+static struct device dev_ac32[MAX_AC32_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_AC32_CARDS] = { 0, };
+static int irq[MAX_AC32_CARDS]  = { 0, };
+static int mem[MAX_AC32_CARDS] = { 0, };
+
+int
+init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+		struct device *dev = &dev_ac32[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];		/* Currently ignored by driver */
+		dev->init = ac3200_probe;
+		/* Default is to only install one card. */
+		if (io[this_dev] == 0 && this_dev != 0) break;
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_ac3200);
+	int this_dev;
 
-	/* If we don't do this, we can't re-insmod it later. */
-	free_irq(dev_ac3200.irq);
-	release_region(dev_ac3200.base_addr, AC_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+		struct device *dev = &dev_ac32[this_dev];
+		if (dev->priv != NULL) {
+			kfree(dev->priv);
+			dev->priv = NULL;
+			/* Someday free_irq + irq2dev may be in ac_close_card() */
+			free_irq(dev->irq);
+			irq2dev_map[dev->irq] = NULL;
+			release_region(dev->base_addr, AC_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v1.3.41/linux/drivers/net/e2100.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/e2100.c	Tue Nov 14 07:48:52 1995
@@ -139,6 +139,7 @@
 {
 	int i, status;
 	unsigned char *station_addr = dev->dev_addr;
+	static unsigned version_printed = 0;
 
 	/* First check the station address for the Ctron prefix. */
 	if (inb(ioaddr + E21_SAPROM + 0) != 0x00
@@ -160,6 +161,15 @@
 	inb(ioaddr + E21_MEDIA); 		/* Point to media selection. */
 	outb(0, ioaddr + E21_ASIC); 	/* and disable the secondary interface. */
 
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("e2100.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
 	printk("%s: E21** at %#3x,", dev->name, ioaddr);
 	for (i = 0; i < 6; i++)
 		printk(" %02X", station_addr[i]);
@@ -178,14 +188,18 @@
 	} else if (dev->irq == 2)	/* Fixup luser bogosity: IRQ2 is really IRQ9 */
 		dev->irq = 9;
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to get memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
 	/* Grab the region so we can find a different board if IRQ select fails. */
 	request_region(ioaddr, E21_IO_EXTENT, "e2100");
 
 	/* The 8390 is at the base address. */
 	dev->base_addr = ioaddr;
 
-	ethdev_init(dev);
-
 	ei_status.name = "E2100";
 	ei_status.word16 = 1;
 	ei_status.tx_start_page = E21_TX_START_PG;
@@ -223,9 +237,6 @@
 	printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
 		   dev->if_port ? "secondary" : "primary", dev->mem_start);
 
-	if (ei_debug > 0)
-		printk(version);
-
 	ei_status.reset_8390 = &e21_reset_8390;
 	ei_status.block_input = &e21_block_input;
 	ei_status.block_output = &e21_block_output;
@@ -241,7 +252,6 @@
 e21_open(struct device *dev)
 {
 	short ioaddr = dev->base_addr;
-	int rc;
 
 	if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) {
 		return EBUSY;
@@ -257,8 +267,7 @@
 	inb(ioaddr + E21_MEM_BASE);
 	outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
 
-	rc = ei_open(dev);
-	if (rc != 0) return rc;
+	ei_open(dev);
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -338,8 +347,8 @@
 	if (ei_debug > 1)
 		printk("%s: Shutting down ethercard.\n", dev->name);
 
-    free_irq(dev->irq);
-    dev->irq = ei_status.saved_irq;
+	free_irq(dev->irq);
+	dev->irq = ei_status.saved_irq;
 
 	/* Shut off the interrupt line and secondary interface. */
 	inb(ioaddr + E21_IRQ_LOW);
@@ -347,9 +356,9 @@
 	inb(ioaddr + E21_IRQ_HIGH); 			/* High IRQ bit, and if_port. */
 	outb(0, ioaddr + E21_ASIC);
 
-    irq2dev_map[dev->irq] = NULL;
+	irq2dev_map[dev->irq] = NULL;
 
-	NS8390_init(dev, 0);
+	ei_close(dev);
 
 	/* Double-check that the memory has been turned off, because really
 	   really bad things happen if it isn't. */
@@ -365,37 +374,70 @@
 {"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list};
 #endif
 
+
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_e2100 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, e2100_probe };
-
-static int io = 0x300;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_E21_CARDS	4	/* Max number of E21 cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, };
+static struct device dev_e21[MAX_E21_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_E21_CARDS] = { 0, };
+static int irq[MAX_E21_CARDS]  = { 0, };
+static int mem[MAX_E21_CARDS] = { 0, };
+static int xcvr[MAX_E21_CARDS] = { 0, };		/* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-	if (io == 0)
-		printk("e2100: You should not use auto-probing with insmod!\n");
-	dev_e2100.base_addr = io;
-	dev_e2100.irq       = irq;
-	if (register_netdev(&dev_e2100) != 0) {
-		printk("e2100: register_netdev() returned non-zero.\n");
-		return -EIO;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+		struct device *dev = &dev_e21[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */
+		dev->init = e2100_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_e2100);
+	int this_dev;
 
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_e2100.base_addr, E21_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+		struct device *dev = &dev_e21[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: e21_close() handles free_irq + irq2dev map */
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(dev->base_addr, E21_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c
--- v1.3.41/linux/drivers/net/hp-plus.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/hp-plus.c	Tue Nov 14 07:48:52 1995
@@ -150,14 +150,21 @@
 	unsigned char checksum = 0;
 	const char *name = "HP-PC-LAN+";
 	int mem_start;
+	static unsigned version_printed = 0;
 
 	/* Check for the HP+ signature, 50 48 0x 53. */
 	if (inw(ioaddr + HP_ID) != 0x4850
 		|| (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
 		return ENODEV;
 
-    if (dev == NULL)
-		dev = init_etherdev(0, sizeof(struct ei_device));
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("hp-plus.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
 
 	printk("%s: %s at %#3x,", dev->name, name, ioaddr);
 
@@ -181,6 +188,12 @@
 		printk(" ID %4.4x", inw(ioaddr + 12));
 	}
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk ("hp-plus.c: unable to allocate memory for dev->priv.\n");
+		return -ENOMEM;
+	 }
+
 	/* Grab the region so we can find another board if something fails. */
 	request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
 
@@ -200,18 +213,14 @@
 		}
 	}
 
-	printk( "%s%s", KERN_INFO, version);
-
 	/* Set the wrap registers for string I/O reads.   */
 	outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 
 	/* Set the base address to point to the NIC, not the "real" base! */
 	dev->base_addr = ioaddr + NIC_OFFSET;
 
-	ethdev_init(dev);
-
-    dev->open = &hpp_open;
-    dev->stop = &hpp_close;
+	dev->open = &hpp_open;
+	dev->stop = &hpp_close;
 
 	ei_status.name = name;
 	ei_status.word16 = 0;		/* Agggghhhhh! Debug time: 2 days! */
@@ -267,7 +276,9 @@
 	/* Select the operational page. */
 	outw(Perf_Page, ioaddr + HP_PAGING);
 
-    return ei_open(dev);
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
 }
 
 static int
@@ -276,13 +287,14 @@
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	int option_reg = inw(ioaddr + HPP_OPTION);
 
-    free_irq(dev->irq);
-    irq2dev_map[dev->irq] = NULL;
-    NS8390_init(dev, 0);
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = NULL;
+	ei_close(dev);
 	outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
 		 ioaddr + HPP_OPTION);
 
-    return 0;
+	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
 static void
@@ -396,39 +408,67 @@
 	return;
 }
 
+
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_hp = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, hp_plus_probe };
-
-static int io = 0x200;
-static int irq = 0;
-
-int init_module(void)
-{
-	if (io == 0)
-		printk("HP-plus: You should not use auto-probing with insmod!\n");
-	dev_hp.base_addr = io;
-	dev_hp.irq       = irq;
-	if (register_netdev(&dev_hp) != 0) {
-		printk("HP-plus: register_netdev() returned non-zero.\n");
-		return -EIO;
+#define MAX_HPP_CARDS	4	/* Max number of HPP cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, };
+static struct device dev_hpp[MAX_HPP_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_HPP_CARDS] = { 0, };
+static int irq[MAX_HPP_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+		struct device *dev = &dev_hpp[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->init = hp_plus_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	int ioaddr = dev_hp.base_addr - NIC_OFFSET;
+	int this_dev;
 
-	unregister_netdev(&dev_hp);
-
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(ioaddr, HP_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+		struct device *dev = &dev_hpp[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: hpp_close() handles free_irq + irq2dev map */
+			int ioaddr = dev->base_addr - NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, HP_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v1.3.41/linux/drivers/net/hp.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/hp.c	Tue Nov 14 07:48:52 1995
@@ -57,6 +57,8 @@
 int hp_probe(struct device *dev);
 int hp_probe1(struct device *dev, int ioaddr);
 
+static int hp_open(struct device *dev);
+static int hp_close(struct device *dev);
 static void hp_reset_8390(struct device *dev);
 static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 					int ring_page);
@@ -106,6 +108,7 @@
 {
 	int i, board_id, wordmode;
 	const char *name;
+	static unsigned version_printed = 0;
 
 	/* Check for the HP physical address, 08 00 09 xx xx xx. */
 	/* This really isn't good enough: we may pick up HP LANCE boards
@@ -126,9 +129,15 @@
 		wordmode = 0;
 	}
 
-	if (dev == NULL)
-		dev = init_etherdev(0, sizeof(struct ei_device));
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("hp.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
 
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+ 
 	printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
 
 	for(i = 0; i < ETHER_ADDR_LEN; i++)
@@ -167,16 +176,20 @@
 		}
 	}
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to get memory for dev->priv.\n");
+		free_irq(dev->irq);
+		return -ENOMEM;
+	}
+
 	/* Grab the region so we can find another board if something fails. */
 	request_region(ioaddr, HP_IO_EXTENT,"hp");
 
-	if (ei_debug > 1)
-		printk(version);
-
 	/* Set the base address to point to the NIC, not the "real" base! */
 	dev->base_addr = ioaddr + NIC_OFFSET;
-
-	ethdev_init(dev);
+	dev->open = &hp_open;
+	dev->stop = &hp_close;
 
 	ei_status.name = name;
 	ei_status.word16 = wordmode;
@@ -193,6 +206,22 @@
 	return 0;
 }
 
+static int
+hp_open(struct device *dev)
+{
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+hp_close(struct device *dev)
+{
+	ei_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
 static void
 hp_reset_8390(struct device *dev)
 {
@@ -348,39 +377,66 @@
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_hp = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, hp_probe };
-
-static int io = 300;
-static int irq = 0;
-
-int init_module(void)
-{
-	if (io == 0)
-		printk("hp: You should not use auto-probing with insmod!\n");
-	dev_hp.base_addr = io;
-	dev_hp.irq       = irq;
-	if (register_netdev(&dev_hp) != 0) {
-		printk("hp: register_netdev() returned non-zero.\n");
-		return -EIO;
+#define MAX_HP_CARDS	4	/* Max number of HP cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, };
+static struct device dev_hp[MAX_HP_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_HP_CARDS] = { 0, };
+static int irq[MAX_HP_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+		struct device *dev = &dev_hp[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->init = hp_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	int ioaddr = dev_hp.base_addr - NIC_OFFSET;
-
-	unregister_netdev(&dev_hp);
+	int this_dev;
 
-	/* If we don't do this, we can't re-insmod it later. */
-	free_irq(dev_hp.irq);
-	release_region(ioaddr, HP_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+		struct device *dev = &dev_hp[this_dev];
+		if (dev->priv != NULL) {
+			int ioaddr = dev->base_addr - NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			free_irq(dev->irq);
+			irq2dev_map[dev->irq] = NULL;
+			release_region(ioaddr, HP_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v1.3.41/linux/drivers/net/ne.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/ne.c	Tue Nov 14 07:48:52 1995
@@ -21,6 +21,7 @@
     Paul Gortmaker	: use ENISR_RDC to monitor Tx PIO uploads, made
 			  sanity checks and bad clone support optional.
     Paul Gortmaker	: new reset code, reset card after probe at boot.
+    Paul Gortmaker	: multiple card support for module users.
 
 */
 
@@ -53,6 +54,9 @@
 /* Do we implement the read before write bugfix ? */
 /* #define NE_RW_BUGFIX */
 
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
 /* ---- No user-serviceable parts below ---- */
 
 /* A zero-terminated list of I/O addresses to be probed. */
@@ -89,6 +93,9 @@
 int ne_probe(struct device *dev);
 static int ne_probe1(struct device *dev, int ioaddr);
 
+static int ne_open(struct device *dev);
+static int ne_close(struct device *dev);
+
 static void ne_reset_8390(struct device *dev);
 static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 			  int ring_page);
@@ -155,6 +162,7 @@
     int start_page, stop_page;
     int neX000, ctron;
     int reg0 = inb_p(ioaddr);
+    static unsigned version_printed = 0;
 
     if (reg0 == 0xFF)
 	return ENODEV;
@@ -173,6 +181,9 @@
 	}
     }
 
+    if (ei_debug  &&  version_printed++ == 0)
+	printk(version);
+
     printk("NE*000 ethercard probe at %#3x:", ioaddr);
 
     /* Reset card. Who knows what dain-bramaged state it was left in. */
@@ -236,11 +247,6 @@
 	stop_page = NE1SM_STOP_PG;
     }
 
-    for(i = 0; i < ETHER_ADDR_LEN; i++) {
-	dev->dev_addr[i] = SA_prom[i];
-	printk(" %2.2x", SA_prom[i]);
-    }
-
     neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
     ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
 
@@ -248,7 +254,7 @@
     if (neX000) {
 	name = (wordlength == 2) ? "NE2000" : "NE1000";
     } else if (ctron) {
-	name = "Cabletron";
+	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
 	start_page = 0x01;
 	stop_page = (wordlength == 2) ? 0x40 : 0x20;
     } else {
@@ -279,9 +285,11 @@
 
     }
 
-
-    if (dev == NULL)
-	dev = init_etherdev(0, sizeof(struct ei_device));
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+	printk("ne.c: Passed a NULL device.\n");
+	dev = init_etherdev(0, 0);
+    }
 
     if (dev->irq < 2) {
 	autoirq_setup(0);
@@ -297,11 +305,16 @@
 	/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
 	   or don't know which one to set. */
 	dev->irq = 9;
+
+    if (! dev->irq) {
+	printk(" failed to detect IRQ line.\n");
+	return EAGAIN;
+    }
     
     /* Snarf the interrupt now.  There's no point in waiting since we cannot
        share and the board will usually be enabled. */
     {
-	int irqval = request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");
+	int irqval = request_irq(dev->irq, ei_interrupt, 0, name);
 	if (irqval) {
 	    printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
 	    return EAGAIN;
@@ -310,18 +323,23 @@
 
     dev->base_addr = ioaddr;
 
-    request_region(ioaddr, NE_IO_EXTENT, wordlength==2 ? "ne2000":"ne1000");
+    /* Allocate dev->priv and fill in 8390 specific dev fields. */
+    if (ethdev_init(dev)) {
+	printk (" unable to get memory for dev->priv.\n");
+	free_irq(dev->irq);
+	return -ENOMEM;
+    }
+ 
+    request_region(ioaddr, NE_IO_EXTENT, name);
 
-    for(i = 0; i < ETHER_ADDR_LEN; i++)
+    for(i = 0; i < ETHER_ADDR_LEN; i++) {
+	printk(" %2.2x", SA_prom[i]);
 	dev->dev_addr[i] = SA_prom[i];
+    }
 
-    ethdev_init(dev);
     printk("\n%s: %s found at %#x, using IRQ %d.\n",
 	   dev->name, name, ioaddr, dev->irq);
 
-    if (ei_debug > 0)
-	printk(version);
-
     ei_status.name = name;
     ei_status.tx_start_page = start_page;
     ei_status.stop_page = stop_page;
@@ -337,10 +355,30 @@
     ei_status.block_input = &ne_block_input;
     ei_status.block_output = &ne_block_output;
     ei_status.get_8390_hdr = &ne_get_8390_hdr;
+    dev->open = &ne_open;
+    dev->stop = &ne_close;
     NS8390_init(dev, 0);
     return 0;
 }
 
+static int
+ne_open(struct device *dev)
+{
+    ei_open(dev);
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int
+ne_close(struct device *dev)
+{
+    if (ei_debug > 1)
+	printk("%s: Shutting down ethercard.\n", dev->name);
+    ei_close(dev);
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
 /* Hard reset the card.  This used to pause for the same period that a
    8390 reset command required, but that shouldn't be necessary. */
 static void
@@ -568,36 +606,71 @@
     return;
 }
 
+
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ne2000 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, ne_probe };
+#define MAX_NE_CARDS	4	/* Max number of NE cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, };
+static struct device dev_ne[MAX_NE_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_NE_CARDS] = { 0, };
+static int irq[MAX_NE_CARDS]  = { 0, };
 
-static int io = 0x300;
-static int irq = 0;
+/* This is set up so that no autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
 
-int init_module(void)
+int
+init_module(void)
 {
-	if (io == 0)
-		printk("ne: You should not use auto-probing with insmod!\n");
-	dev_ne2000.base_addr = io;
-	dev_ne2000.irq       = irq;
-	if (register_netdev(&dev_ne2000) != 0)
-		return -EIO;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		struct device *dev = &dev_ne[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->init = ne_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only complain once */
+			printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+			return -EPERM;
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
+	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_ne2000);
+	int this_dev;
 
-	/* If we don't do this, we can't re-insmod it later. */
-	free_irq(dev_ne2000.irq);
-	release_region(dev_ne2000.base_addr, NE_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		struct device *dev = &dev_ne[this_dev];
+		if (dev->priv != NULL) {
+			kfree(dev->priv);
+			dev->priv = NULL;
+			free_irq(dev->irq);
+			irq2dev_map[dev->irq] = NULL;
+			release_region(dev->base_addr, NE_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.3.41/linux/drivers/net/net_init.c	Wed Nov  8 07:11:33 1995
+++ linux/drivers/net/net_init.c	Tue Nov 14 16:03:52 1995
@@ -132,8 +132,9 @@
 }
 
 
-static int eth_mac_addr(struct device *dev, struct sockaddr *addr)
+static int eth_mac_addr(struct device *dev, void *p)
 {
+	struct sockaddr *addr=p;
 	if(dev->start)
 		return -EBUSY;
 	memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
@@ -164,7 +165,8 @@
 	dev->hard_header	= eth_header;
 	dev->rebuild_header = eth_rebuild_header;
 	dev->set_mac_address = eth_mac_addr;
-	dev->header_cache = eth_header_cache;
+	dev->header_cache_bind = eth_header_cache_bind;
+	dev->header_cache_update = eth_header_cache_update;
 
 	dev->type		= ARPHRD_ETHER;
 	dev->hard_header_len = ETH_HLEN;
diff -u --recursive --new-file v1.3.41/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v1.3.41/linux/drivers/net/smc-ultra.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/net/smc-ultra.c	Tue Nov 14 07:48:52 1995
@@ -35,6 +35,10 @@
 	This driver does not support the programmed-I/O data transfer mode of
 	the EtherEZ.  That support (if available) is smc-ez.c.  Nor does it
 	use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+	Changelog:
+
+	Paul Gortmaker	: multiple card support for module users.
 */
 
 static const char *version =
@@ -117,6 +121,7 @@
 	int checksum = 0;
 	const char *model_name;
 	unsigned char eeprom_irq = 0;
+	static unsigned version_printed = 0;
 	/* Values from various config regs. */
 	unsigned char num_pages, irqreg, addr;
 	unsigned char idreg = inb(ioaddr + 7);
@@ -135,10 +140,14 @@
 	if ((checksum & 0xff) != 0xFF)
 		return ENODEV;
 
-	if (dev == NULL)
-		dev = init_etherdev(0, sizeof(struct ei_device));
-	if (dev == NULL) /* Still.. */
-		return ENOMEM; /* Out of memory ?? */
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("smc-ultra.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
 
 	model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
 
@@ -175,7 +184,12 @@
 		eeprom_irq = 1;
 	}
 
-
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (", no memory for dev->priv.\n");
+                return -ENOMEM;
+        }
+ 
 	/* OK, we are certain this is going to work.  Setup the device. */
 	request_region(ioaddr, ULTRA_IO_EXTENT, model_name);
 
@@ -190,8 +204,6 @@
 		num_pages = num_pages_tbl[(addr >> 4) & 3];
 	}
 
-	ethdev_init(dev);
-
 	ei_status.name = model_name;
 	ei_status.word16 = 1;
 	ei_status.tx_start_page = START_PG;
@@ -204,8 +216,6 @@
 
 	printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
 		   dev->irq, dev->mem_start, dev->mem_end-1);
-	if (ei_debug > 0)
-		printk(version);
 
 	ei_status.reset_8390 = &ultra_reset_8390;
 	ei_status.block_input = &ultra_block_input;
@@ -222,7 +232,6 @@
 ultra_open(struct device *dev)
 {
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
-	int rc;
 
 	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name))
 		return -EAGAIN;
@@ -230,8 +239,7 @@
 	outb(ULTRA_MEMENB, ioaddr);	/* Enable memory, 16 bit mode. */
 	outb(0x80, ioaddr + 5);
 	outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
-	rc = ei_open(dev);
-	if (rc != 0) return rc;
+	ei_open(dev);
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -330,39 +338,67 @@
 	return 0;
 }
 
+
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ultra = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, ultra_probe };
-
-static int io = 0x200;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_ULTRA_CARDS	4	/* Max number of Ultra cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_ULTRA_CARDS] = { 0, };
+static int irq[MAX_ULTRA_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-	if (io == 0)
-		printk("smc-ultra: You should not use auto-probing with insmod!\n");
-	dev_ultra.base_addr = io;
-	dev_ultra.irq       = irq;
-	if (register_netdev(&dev_ultra) != 0) {
-		printk("smc-ultra: register_netdev() returned non-zero.\n");
-		return -EIO;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->init = ultra_probe;
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
 	}
+
 	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	int ioaddr = dev_ultra.base_addr - ULTRA_NIC_OFFSET;
-
-	unregister_netdev(&dev_ultra);
+	int this_dev;
 
-	/* If we don't do this, we can't re-insmod it later. */
-    	release_region(ioaddr, ULTRA_IO_EXTENT);
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: ultra_close_card() does free_irq + irq2dev */
+			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, ULTRA_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.41/linux/drivers/net/tunnel.c linux/drivers/net/tunnel.c
--- v1.3.41/linux/drivers/net/tunnel.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/net/tunnel.c	Tue Nov 14 16:03:52 1995
@@ -88,7 +88,7 @@
 	dev->addr_len=0;
 	dev->hard_header_len=0;
 	dev->hard_header=NULL;
-	dev->header_cache=NULL;
+	dev->header_cache_bind=NULL;
 	dev->rebuild_header=NULL;
 	/* End of stomp 8) */
 	return 0;
@@ -271,7 +271,6 @@
 }
 
 #ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
 
 static int tunnel_probe(struct device *dev)
 {
diff -u --recursive --new-file v1.3.41/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v1.3.41/linux/drivers/net/wd.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/net/wd.c	Tue Nov 14 07:48:52 1995
@@ -18,7 +18,9 @@
 
 	Changelog:
 
-	Paul Gortmaker	: multiple card support for module users
+	Paul Gortmaker	: multiple card support for module users, support
+			  for non-standard memory sizes.
+			 
 
 */
 
@@ -121,8 +123,11 @@
 		|| (checksum & 0xff) != 0xFF)
 		return ENODEV;
 
-	if (dev == NULL)
-		dev = init_etherdev(0, sizeof(struct ei_device));
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("wd.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
 
 	if (ei_debug  &&  version_printed++ == 0)
 		printk(version);
@@ -246,20 +251,32 @@
 		return EAGAIN;
 	}
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {	
+		printk (" unable to get memory for dev->priv.\n");
+		free_irq(dev->irq);
+		return -ENOMEM;
+	}
+
 	/* OK, were are certain this is going to work.  Setup the device. */
 	request_region(ioaddr, WD_IO_EXTENT,"wd");
-	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;
+
+	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+	if (dev->mem_end != 0) {
+		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+	} else {
+		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+	}
+	dev->rmem_end = dev->mem_end;
 
 	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
 		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
@@ -286,7 +303,6 @@
 wd_open(struct device *dev)
 {
   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
-  int rc;
 
   /* Map in the shared memory. Always set register 0 last to remain
 	 compatible with very old boards. */
@@ -297,8 +313,7 @@
 	  outb(ei_status.reg5, ioaddr+WD_CMDREG5);
   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
 
-  rc = ei_open(dev);
-  if (rc != 0) return rc;
+  ei_open(dev);
   MOD_INC_USE_COUNT;
   return 0;
 }
@@ -392,11 +407,11 @@
 
 	if (ei_debug > 1)
 		printk("%s: Shutting down ethercard.\n", dev->name);
-	NS8390_init(dev, 0);
-	dev->start = 0;
+	ei_close(dev);
 
 	/* Change from 16-bit to 8-bit shared memory so reboot works. */
-	outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+	if (ei_status.word16)
+		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
 
 	/* And disable the shared memory. */
 	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
@@ -406,12 +421,12 @@
 	return 0;
 }
 
-
+
 #ifdef MODULE
-#define MAX_WD_MODS	4	/* Max number of wd modules allowed */
-#define NAMELEN		9	/* # of chars for storing dev->name */
-static char namelist[NAMELEN * MAX_WD_MODS] = { 0, };
-static struct device dev_wd80x3[MAX_WD_MODS] = {
+#define MAX_WD_CARDS	4	/* Max number of wd cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, };
+static struct device dev_wd[MAX_WD_CARDS] = {
 	{
 		NULL,		/* assign a chunk of namelist[] below */
 		0, 0, 0, 0,
@@ -420,31 +435,36 @@
 	},
 };
 
-static int io[MAX_WD_MODS] = { 0, };
-static int irq[MAX_WD_MODS]  = { 0, };
-static int mem[MAX_WD_MODS] = { 0, };
+static int io[MAX_WD_CARDS] = { 0, };
+static int irq[MAX_WD_CARDS]  = { 0, };
+static int mem[MAX_WD_CARDS] = { 0, };
+static int mem_end[MAX_WD_CARDS] = { 0, };	/* for non std. mem size */
 
 /* This is set up so that only a single autoprobe takes place per call.
 ISA device autoprobes on a running machine are not recommended. */
 int
 init_module(void)
 {
-	int this_dev;
+	int this_dev, found = 0;
 
-	for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
-		dev_wd80x3[this_dev].name = namelist+(NAMELEN*this_dev);
-		dev_wd80x3[this_dev].irq = irq[this_dev];
-		dev_wd80x3[this_dev].base_addr = io[this_dev];
-		dev_wd80x3[this_dev].mem_start = mem[this_dev];
-		dev_wd80x3[this_dev].init = wd_probe;
+	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+		struct device *dev = &dev_wd[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		dev->mem_end = mem_end[this_dev];
+		dev->init = wd_probe;
 		if (io[this_dev] == 0)  {
 			if (this_dev != 0) break; /* only autoprobe 1st one */
 			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
 		}
-		if (register_netdev(&dev_wd80x3[this_dev]) != 0) {
-			printk(KERN_WARNING "modules: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
-			return -EIO;
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
 		}
+		found++;
 	}
 
 	return 0;
@@ -455,12 +475,16 @@
 {
 	int this_dev;
 
-	for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
-		if (dev_wd80x3[this_dev].priv != NULL) {
-			int ioaddr = dev_wd80x3[this_dev].base_addr - WD_NIC_OFFSET;
-			unregister_netdev(&dev_wd80x3[this_dev]);
-			free_irq(dev_wd80x3[this_dev].irq);
+	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+		struct device *dev = &dev_wd[this_dev];
+		if (dev->priv != NULL) {
+			int ioaddr = dev->base_addr - WD_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			free_irq(dev->irq);
+			irq2dev_map[dev->irq] = NULL;
 			release_region(ioaddr, WD_IO_EXTENT);
+			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v1.3.41/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v1.3.41/linux/drivers/pci/pci.c	Mon Nov 13 12:36:44 1995
+++ linux/drivers/pci/pci.c	Tue Nov 14 07:35:31 1995
@@ -289,22 +289,6 @@
 	}
 }
 
-
-const char *pci_strbioserr (int error)
-{
-	switch (error) {
-	      case PCIBIOS_SUCCESSFUL:		return "SUCCESSFUL";
-	      case PCIBIOS_FUNC_NOT_SUPPORTED:	return "FUNC_NOT_SUPPORTED";
-	      case PCIBIOS_BAD_VENDOR_ID:	return "SUCCESSFUL";
-	      case PCIBIOS_DEVICE_NOT_FOUND:	return "DEVICE_NOT_FOUND";
-	      case PCIBIOS_BAD_REGISTER_NUMBER:	return "BAD_REGISTER_NUMBER";
-	      case PCIBIOS_SET_FAILED:		return "SET_FAILED";
-	      case PCIBIOS_BUFFER_TOO_SMALL:	return "BUFFER_TOO_SMALL";
-	      default:				return "Unknown error status";
-	}
-}
-
-
 const char *pci_strclass (unsigned int class)
 {
 	switch (class >> 8) {
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v1.3.41/linux/drivers/scsi/53c7,8xx.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/scsi/53c7,8xx.c	Tue Nov 14 07:35:31 1995
@@ -894,7 +894,7 @@
 	    &irq))) {
 	printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
 		"        perhaps you specified an incorrect PCI bus, device, or function.\n"
-		, pci_strbioserr(error));
+		, pcibios_strerror(error));
 	return -1;
     }
 
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v1.3.41/linux/drivers/scsi/BusLogic.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/BusLogic.c	Mon Nov 13 21:57:50 1995
@@ -0,0 +1,2577 @@
+/*
+
+  Linux Driver for BusLogic SCSI Host Adapters
+
+  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation, provided that none of the source code or runtime
+  copyright notices are removed or modified.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that all modifications to this software be
+  sent directly to him for evaluation and testing.
+
+  Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable,
+  and to David B. Gentzel, for writing the original Linux BusLogic driver.
+
+*/
+
+
+#define BusLogic_DriverVersion		"1.3.0"
+#define BusLogic_DriverDate		"13 November 1995"
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "BusLogic.h"
+
+
+/*
+  BusLogic_CommandLineEntryCount is a count of the number of "BusLogic="
+  entries provided on the Linux Kernel Command Line.
+*/
+
+static int
+  BusLogic_CommandLineEntryCount =	0;
+
+
+/*
+  BusLogic_CommandLineEntries is an array of Command Line Entry structures
+  representing the "BusLogic=" entries provided on the Linux Kernel Command
+  Line.
+*/
+
+static BusLogic_CommandLineEntry_T
+  BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters];
+
+
+/*
+  BusLogic_TracingOptions is a bit mask of Tracing Options to be applied
+  across all Host Adapters.
+*/
+
+static int
+  BusLogic_TracingOptions =		0;
+
+
+/*
+  BusLogic_RegisteredHostAdapters is a linked list of all the registered
+  BusLogic Host Adapters.
+*/
+
+static BusLogic_HostAdapter_T
+  *BusLogic_RegisteredHostAdapters =	NULL;
+
+
+/*
+  BusLogic_Standard_IO_Addresses is the list of standard I/O Addresses at which
+  BusLogic Host Adapters may potentially be found.
+*/
+
+static unsigned short
+  BusLogic_IO_StandardAddresses[] =
+    { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 };
+
+
+/*
+  BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for
+  potential BusLogic Host Adapters.  It is initialized by interrogating the
+  PCI Configuration Space on PCI machines as well as from the list of
+  standard BusLogic I/O Addresses.
+*/
+
+static unsigned short
+  BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] =   { 0 };
+
+
+/*
+  BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using
+  a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared
+  interrupts.  Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by
+  BusLogic Host Adapters.
+*/
+
+static short
+  BusLogic_IRQ_UsageCount[7] =		{ 0 };
+
+
+/*
+  BusLogic_CommandFailureReason holds a string identifying the reason why a
+  call to BusLogic_Command failed.  It is only valid when BusLogic_Command
+  returns a failure code.
+*/
+
+static char
+  *BusLogic_CommandFailureReason;
+
+
+/*
+  BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
+*/
+
+static struct proc_dir_entry
+  BusLogic_ProcDirectoryEntry =
+    { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+
+/*
+  BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+  Name, Copyright Notice, and Contact Address.
+*/
+
+static void BusLogic_AnnounceDriver(void)
+{
+  static boolean DriverAnnouncementPrinted = false;
+  if (DriverAnnouncementPrinted) return;
+  printk("scsi: ***** BusLogic SCSI Driver Version "
+	 BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n");
+  printk("scsi: Copyright 1995 by Leonard N. Zubkoff "
+	 "<lnz@dandelion.com>\n");
+  DriverAnnouncementPrinted = true;
+}
+
+
+/*
+  BusLogic_DriverInfo returns the Board Name to identify this SCSI Driver
+  and Host Adapter.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Host->hostdata;
+  return HostAdapter->BoardName;
+}
+
+
+/*
+  BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses
+  to be probed for potential BusLogic SCSI Host Adapters by interrogating the
+  PCI Configuration Space on PCI machines as well as from the list of standard
+  BusLogic I/O Addresses.
+*/
+
+static void BusLogic_InitializeAddressProbeList(void)
+{
+  int DestinationIndex = 0, SourceIndex = 0;
+  /*
+    If BusLogic_Setup has been called, do not override the Kernel Command
+    Line specifications.
+  */
+  if (BusLogic_IO_AddressProbeList[0] != 0) return;
+#ifdef CONFIG_PCI
+  /*
+    Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters.
+  */
+  if (pcibios_present())
+    {
+      unsigned short Index = 0, VendorID;
+      unsigned char Bus, DeviceAndFunction;
+      unsigned int BaseAddress0;
+      while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++,
+				&Bus, &DeviceAndFunction) == 0)
+	if (pcibios_read_config_word(Bus, DeviceAndFunction,
+				     PCI_VENDOR_ID, &VendorID) == 0 &&
+	    VendorID == PCI_VENDOR_ID_BUSLOGIC &&
+	    pcibios_read_config_dword(Bus, DeviceAndFunction,
+				      PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+	    (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) ==
+	      PCI_BASE_ADDRESS_SPACE_IO)
+	  {
+	    BusLogic_IO_AddressProbeList[DestinationIndex++] =
+	      BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	  }
+    }
+#endif
+  /*
+    Append the list of standard BusLogic I/O Addresses.
+  */
+  while (DestinationIndex < BusLogic_IO_MaxProbeAddresses &&
+	 BusLogic_IO_StandardAddresses[SourceIndex] > 0)
+    BusLogic_IO_AddressProbeList[DestinationIndex++] =
+      BusLogic_IO_StandardAddresses[SourceIndex++];
+  BusLogic_IO_AddressProbeList[DestinationIndex] = 0;
+}
+
+
+/*
+  BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered
+  BusLogic Host Adapters.
+*/
+
+static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  HostAdapter->Next = NULL;
+  if (BusLogic_RegisteredHostAdapters != NULL)
+    {
+      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      BusLogic_HostAdapter_T *NextHostAdapter;
+      while ((NextHostAdapter = LastHostAdapter->Next) != NULL)
+	LastHostAdapter = NextHostAdapter;
+      LastHostAdapter->Next = HostAdapter;
+    }
+  else BusLogic_RegisteredHostAdapters = HostAdapter;
+}
+
+
+/*
+  BusLogic_UnregisterHostAdapter removes Host Adapter from the list of
+  registered BusLogic Host Adapters.
+*/
+
+static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  if (BusLogic_RegisteredHostAdapters != HostAdapter)
+    {
+      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter)
+	LastHostAdapter = LastHostAdapter->Next;
+      if (LastHostAdapter != NULL)
+	LastHostAdapter->Next = HostAdapter->Next;
+    }
+  else BusLogic_RegisteredHostAdapters = HostAdapter->Next;
+  HostAdapter->Next = NULL;
+}
+
+
+/*
+  BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs)
+  for Host Adapter.
+*/
+
+static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+  int i;
+  for (i = 0; i < BusLogic_InitialCCBs; i++)
+    {
+      BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+	scsi_init_malloc(sizeof(BusLogic_CCB_T), GFP_ATOMIC | GFP_DMA);
+      if (CCB == NULL)
+	{
+	  printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n",
+		 HostAdapter->HostNumber, i);
+	  return false;
+	}
+      memset(CCB, 0, sizeof(BusLogic_CCB_T));
+      CCB->HostAdapter = HostAdapter;
+      CCB->Status = BusLogic_CCB_Free;
+      CCB->Next = HostAdapter->Free_CCBs;
+      CCB->NextAll = HostAdapter->All_CCBs;
+      HostAdapter->Free_CCBs = CCB;
+      HostAdapter->All_CCBs = CCB;
+    }
+  return true;
+}
+
+
+/*
+  BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB;
+  HostAdapter->All_CCBs = NULL;
+  HostAdapter->Free_CCBs = NULL;
+  while ((CCB = NextCCB) != NULL)
+    {
+      NextCCB = CCB->NextAll;
+      scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T));
+    }
+}
+
+
+/*
+  BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list,
+  allocating more memory from the Kernel if necessary.
+*/
+
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
+{
+  static unsigned int SerialNumber = 0;
+  BusLogic_CCB_T *CCB;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB = HostAdapter->Free_CCBs;
+  if (CCB != NULL)
+    {
+      CCB->SerialNumber = SerialNumber++;
+      HostAdapter->Free_CCBs = CCB->Next;
+      BusLogic_UnlockHostAdapter(HostAdapter);
+      return CCB;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  CCB = (BusLogic_CCB_T *) scsi_init_malloc(sizeof(BusLogic_CCB_T),
+					    GFP_ATOMIC | GFP_DMA);
+  if (CCB == NULL)
+    {
+      printk("scsi%d: Failed to allocate an additional CCB\n",
+	     HostAdapter->HostNumber);
+      return NULL;
+    }
+  printk("scsi%d: Allocated an additional CCB\n", HostAdapter->HostNumber);
+  memset(CCB, 0, sizeof(BusLogic_CCB_T));
+  CCB->HostAdapter = HostAdapter;
+  CCB->Status = BusLogic_CCB_Free;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB->SerialNumber = SerialNumber++;
+  CCB->NextAll = HostAdapter->All_CCBs;
+  HostAdapter->All_CCBs = CCB;
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return CCB;
+}
+
+
+/*
+  BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+  free list.
+*/
+
+static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
+{
+  BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB->Command = NULL;
+  CCB->Status = BusLogic_CCB_Free;
+  CCB->SerialNumber = 0;
+  CCB->Next = HostAdapter->Free_CCBs;
+  HostAdapter->Free_CCBs = CCB;
+  BusLogic_UnlockHostAdapter(HostAdapter);
+}
+
+
+/*
+  BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+  providing ParameterLength bytes of ParameterData and receiving at most
+  ReplyLength bytes of ReplyData; any excess reply data is received but
+  discarded.
+
+  On success, this function returns the number of reply bytes read from
+  the Host Adapter (including any discarded data); on failure, it returns
+  -1 if the command was invalid, or -2 if a timeout occurred.
+
+  This function is only called during board detection and initialization, so
+  performance and latency are not critical, and exclusive access to the Host
+  Adapter hardware is assumed.  Once the board and driver are initialized, the
+  only Host Adapter command that is issued is the single byte Start Mailbox
+  Scan command, which does not require waiting for the Host Adapter Ready bit
+  to be set in the Status Register.
+*/
+
+static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
+			    BusLogic_OperationCode_T OperationCode,
+			    void *ParameterData,
+			    int ParameterLength,
+			    void *ReplyData,
+			    int ReplyLength)
+{
+  unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+  unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+  unsigned char StatusRegister = 0, InterruptRegister;
+  long TimeoutCounter;
+  int ReplyBytes = 0;
+  /*
+    Clear out the Reply Data if provided.
+  */
+  if (ReplyLength > 0)
+    memset(ReplyData, 0, ReplyLength);
+  /*
+    Select an appropriate timeout value.
+  */
+  switch (OperationCode)
+    {
+    case BusLogic_InquireInstalledDevicesID0to7:
+    case BusLogic_InquireInstalledDevicesID8to15:
+      /* Approximately 60 seconds. */
+      TimeoutCounter = loops_per_sec << 2;
+      break;
+    default:
+      /* Approximately 1 second. */
+      TimeoutCounter = loops_per_sec >> 4;
+      break;
+    }
+  /*
+    Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+    Register Busy bit to be reset in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if ((StatusRegister & BusLogic_HostAdapterReady) &&
+	  !(StatusRegister & BusLogic_CommandParameterRegisterBusy))
+	break;
+    }
+  BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+  if (TimeoutCounter < 0) return -2;
+  /*
+    Write the OperationCode to the Command/Parameter Register.
+  */
+  BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+  /*
+    Write any additional Parameter Bytes.
+  */
+  HostAdapter->HostAdapterCommandCompleted = false;
+  while (--ParameterLength >= 0)
+    {
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_CommandComplete) break;
+      if (HostAdapter->HostAdapterCommandCompleted) break;
+      while (--TimeoutCounter >= 0)
+	{
+	  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+	  if (!(StatusRegister & BusLogic_CommandParameterRegisterBusy)) break;
+	}
+      BusLogic_CommandFailureReason =
+	"Timeout waiting for Parameter Acceptance";
+      if (TimeoutCounter < 0) return -2;
+      BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+    }
+  BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+  if (ParameterLength >= 0) return -1;
+  /*
+    The Modify I/O Address command does not cause a Command Complete Interrupt.
+  */
+  if (OperationCode == BusLogic_ModifyIOAddress)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+      return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : 0);
+    }
+  /*
+    Receive any Reply Bytes, waiting for either the Command Complete bit to
+    be set in the Interrupt Register, or for the Interrupt Handler to set the
+    HostAdapterCommandCompleted bit in the Host Adapter structure.
+  */
+  HostAdapter->HostAdapterCommandCompleted = false;
+  while (--TimeoutCounter >= 0)
+    {
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_CommandComplete) break;
+      if (HostAdapter->HostAdapterCommandCompleted) break;
+      if (StatusRegister & BusLogic_DataInRegisterReady)
+	if (++ReplyBytes <= ReplyLength)
+	  *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+	else BusLogic_ReadDataInRegister(HostAdapter);
+  }
+  BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+  if (TimeoutCounter < 0) return -2;
+  /*
+    Clear any pending Command Complete Interrupt, unless this is a
+    Test Command Complete Interrupt command.
+  */
+  if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+    BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+  if (BusLogic_TracingOptions & BusLogic_TraceConfiguration)
+    if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+      {
+	int i;
+	printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+	       OperationCode, StatusRegister, ReplyLength, ReplyBytes);
+	for (i = 0; i < ReplyBytes; i++)
+	  printk(" %02X", ((unsigned char *) ReplyData)[i]);
+	printk("\n");
+      }
+  /*
+    Return count of Reply Bytes, or -1 if the command was invalid.
+  */
+  BusLogic_CommandFailureReason = "Command Invalid";
+  return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : ReplyBytes);
+}
+
+
+/*
+  BusLogic_Failure prints a standardized error message for tests that are
+  executed before the SCSI Host is registered, and then returns false.
+*/
+
+static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
+				char *ErrorMessage)
+{
+  BusLogic_AnnounceDriver();
+  printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n",
+	 HostAdapter->IO_Address);
+  printk("%s FAILED - DETACHING\n", ErrorMessage);
+  if (BusLogic_CommandFailureReason != NULL)
+    printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason);
+  return false;
+}
+
+
+/*
+  BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+*/
+
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  boolean TraceProbe = (BusLogic_TracingOptions & BusLogic_TraceProbe);
+  unsigned char StatusRegister, GeometryRegister;
+  /*
+    Read the Status Register to test if there is an I/O port that responds.  A
+    nonexistent I/O port will return 0xFF, in which case there is definitely no
+    BusLogic Host Adapter at this base I/O Address.
+  */
+  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+  if (TraceProbe)
+    printk("BusLogic_Probe(0x%X): Status 0x%02X\n",
+	   HostAdapter->IO_Address, StatusRegister);
+  if (StatusRegister == 0xFF) return false;
+  /*
+    Read the undocumented BusLogic Geometry Register to test if there is an I/O
+    port that responds.  Adaptec Host Adapters do not implement the Geometry
+    Register, so this test helps serve to avoid incorrectly recognizing an
+    Adaptec 1542A or 1542B as a BusLogic.  Unfortunately, the Adaptec 1542C
+    series does respond to the Geometry Register I/O port, but it will be
+    rejected later when the Inquire Extended Setup Information command is
+    issued in BusLogic_CheckHostAdapter.  The AMI FastDisk Host Adapter is a
+    BusLogic clone that implements the same interface as earlier BusLogic
+    boards, including the undocumented commands, and is therefore supported by
+    this driver.  However, the AMI FastDisk always returns 0x00 upon reading
+    the Geometry Register, so the extended translation option should always be
+    left disabled on the AMI FastDisk.
+  */
+  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+  if (TraceProbe)
+    printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n",
+	   HostAdapter->IO_Address, GeometryRegister);
+  if (GeometryRegister == 0xFF) return false;
+  /*
+    Indicate the Host Adapter Probe completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter,
+  and waits for Host Adapter Diagnostics to complete.
+*/
+
+static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
+					     *HostAdapter)
+{
+  boolean TraceHardReset = (BusLogic_TracingOptions & BusLogic_TraceHardReset);
+  long TimeoutCounter = loops_per_sec >> 2;
+  unsigned char StatusRegister = 0;
+  /*
+    Issue a Hard Reset Command to the Host Adapter.  The Host Adapter should
+    respond by setting Diagnostic Active in the Status Register.
+  */
+  BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset);
+  /*
+    Wait until Diagnostic Active is set in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if ((StatusRegister & BusLogic_DiagnosticActive)) break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n",
+	   HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    Wait until Diagnostic Active is reset in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (!(StatusRegister & BusLogic_DiagnosticActive)) break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n",
+	   HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
+    or Data In Register Ready bits is set in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister & (BusLogic_DiagnosticFailure |
+			    BusLogic_HostAdapterReady |
+			    BusLogic_DataInRegisterReady))
+	break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n",
+	   HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    If Diagnostic Failure is set or Host Adapter Ready is reset, then an
+    error occurred during the Host Adapter diagnostics.  If Data In Register
+    Ready is set, then there is an Error Code available.
+  */
+  if ((StatusRegister & BusLogic_DiagnosticFailure) ||
+      !(StatusRegister & BusLogic_HostAdapterReady))
+    {
+      BusLogic_CommandFailureReason = NULL;
+      BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
+      printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister);
+      if (StatusRegister & BusLogic_DataInRegisterReady)
+	{
+	  unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
+	  printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode);
+	}
+      return false;
+    }
+  /*
+    Indicate the Host Adapter Hard Reset completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+  Host Adapter.
+*/
+
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  unsigned long ProcessorFlags;
+  int Result;
+  /*
+    Issue the Inquire Setup Information command.  Only genuine BusLogic Host
+    Adapters and true clones support this command.  Adaptec 1542C series Host
+    Adapters that respond to the Geometry Register I/O port will fail this
+    command.  Interrupts must be disabled around the call to BusLogic_Command
+    since a Command Complete interrupt could occur if the IRQ Channel was
+    previously enabled for another BusLogic Host Adapter sharing the same IRQ
+    Channel.
+  */
+  save_flags(ProcessorFlags);
+  cli();
+  RequestedReplyLength = sizeof(ExtendedSetupInformation);
+  Result = BusLogic_Command(HostAdapter,
+			    BusLogic_InquireExtendedSetupInformation,
+			    &RequestedReplyLength, sizeof(RequestedReplyLength),
+			    &ExtendedSetupInformation,
+			    sizeof(ExtendedSetupInformation));
+  restore_flags(ProcessorFlags);
+  if (BusLogic_TracingOptions & BusLogic_TraceProbe)
+    printk("BusLogic_Check(0x%X): Result %d\n",
+	   HostAdapter->IO_Address, Result);
+  return (Result == sizeof(ExtendedSetupInformation));
+}
+
+
+/*
+  BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+  from Host Adapter.
+*/
+
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+						     *HostAdapter)
+{
+  BusLogic_BoardID_T BoardID;
+  BusLogic_Configuration_T Configuration;
+  BusLogic_SetupInformation_T SetupInformation;
+  BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+  BusLogic_ModelAndRevision_T ModelAndRevision;
+  BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
+  BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  unsigned char GeometryRegister, *TargetPointer, Character;
+  unsigned short AllTargetsMask, DisconnectPermitted;
+  unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault;
+  boolean CommonErrorRecovery;
+  int TargetID, i;
+  /*
+    Issue the Inquire Board ID command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0,
+		       &BoardID, sizeof(BoardID)) != sizeof(BoardID))
+    return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+  /*
+    Issue the Inquire Configuration command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0,
+		       &Configuration, sizeof(Configuration))
+      != sizeof(Configuration))
+    return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+  /*
+    Issue the Inquire Setup Information command.
+  */
+  RequestedReplyLength = sizeof(SetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+		       &RequestedReplyLength, sizeof(RequestedReplyLength),
+		       &SetupInformation, sizeof(SetupInformation))
+      != sizeof(SetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+  /*
+    Issue the Inquire Extended Setup Information command.
+  */
+  RequestedReplyLength = sizeof(ExtendedSetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation,
+		       &RequestedReplyLength, sizeof(RequestedReplyLength),
+		       &ExtendedSetupInformation,
+		       sizeof(ExtendedSetupInformation))
+      != sizeof(ExtendedSetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+  /*
+    Issue the Inquire Board Model and Revision command.
+  */
+  RequestedReplyLength = sizeof(ModelAndRevision);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelAndRevision,
+		       &RequestedReplyLength, sizeof(RequestedReplyLength),
+		       &ModelAndRevision, sizeof(ModelAndRevision))
+      != sizeof(ModelAndRevision))
+    return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL AND REVISION");
+  /*
+    Issue the Inquire Firmware Version 3rd Digit command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+		       NULL, 0, &FirmwareVersion3rdDigit,
+		       sizeof(FirmwareVersion3rdDigit))
+      != sizeof(FirmwareVersion3rdDigit))
+    return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+  /*
+    Issue the Inquire Firmware Version Letter command.
+  */
+  FirmwareVersionLetter = '\0';
+  if (BoardID.FirmwareVersion1stDigit >= '3')
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+			 NULL, 0, &FirmwareVersionLetter,
+			 sizeof(FirmwareVersionLetter))
+	!= sizeof(FirmwareVersionLetter))
+      return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+  /*
+    BusLogic Host Adapters can be identified by their model number and
+    the major version number of their firmware as follows:
+
+    4.xx	BusLogic "C" Series Host Adapters:
+		  946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+    3.xx	BusLogic "S" Series Host Adapters:
+		  747S/747D/757S/757D/445S/545S/542D
+		  542B/742A (revision H)
+    2.xx	BusLogic "A" Series Host Adapters:
+		  542B/742A (revision G and below)
+    0.xx	AMI FastDisk VLB BusLogic Clone Host Adapter
+  */
+  /*
+    Save the Model Name and Board Name in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->ModelName;
+  for (i = 0; i < sizeof(ModelAndRevision.Model); i++)
+    {
+      Character = ModelAndRevision.Model[i];
+      if (Character == ' ' || Character == '\0') break;
+      *TargetPointer++ = Character;
+    }
+  *TargetPointer++ = '\0';
+  strcpy(HostAdapter->BoardName, "BusLogic ");
+  strcat(HostAdapter->BoardName, HostAdapter->ModelName);
+  strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
+  /*
+    Save the Firmware Version in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->FirmwareVersion;
+  *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+  *TargetPointer++ = '.';
+  *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+  if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+    *TargetPointer++ = FirmwareVersion3rdDigit;
+  if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+    *TargetPointer++ = FirmwareVersionLetter;
+  *TargetPointer++ = '\0';
+  /*
+    Determine the IRQ Channel and save it in the Host Adapter structure.
+  */
+  if (Configuration.IRQ_Channel9)
+    HostAdapter->IRQ_Channel = 9;
+  else if (Configuration.IRQ_Channel10)
+    HostAdapter->IRQ_Channel = 10;
+  else if (Configuration.IRQ_Channel11)
+    HostAdapter->IRQ_Channel = 11;
+  else if (Configuration.IRQ_Channel12)
+    HostAdapter->IRQ_Channel = 12;
+  else if (Configuration.IRQ_Channel14)
+    HostAdapter->IRQ_Channel = 14;
+  else if (Configuration.IRQ_Channel15)
+    HostAdapter->IRQ_Channel = 15;
+  /*
+    Determine the DMA Channel and save it in the Host Adapter structure.
+  */
+  if (Configuration.DMA_Channel5)
+    HostAdapter->DMA_Channel = 5;
+  else if (Configuration.DMA_Channel6)
+    HostAdapter->DMA_Channel = 6;
+  else if (Configuration.DMA_Channel7)
+    HostAdapter->DMA_Channel = 7;
+  /*
+    Save the Host Adapter SCSI ID in the Host Adapter structure.
+  */
+  HostAdapter->SCSI_ID = Configuration.HostAdapterID;
+  /*
+    Save the Synchronous Initiation flag and SCSI Parity Checking flag
+    in the Host Adapter structure.
+  */
+  HostAdapter->SynchronousInitiation =
+    SetupInformation.SynchronousInitiationEnabled;
+  HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled;
+  /*
+    Determine the Bus Type and save it in the Host Adapter structure,
+    overriding the DMA Channel if it is inappropriate for the bus type.
+  */
+  if (ExtendedSetupInformation.BusType == 'A')
+    HostAdapter->BusType = BusLogic_ISA_Bus;
+  else
+    switch (HostAdapter->ModelName[0])
+      {
+      case '4':
+	HostAdapter->BusType = BusLogic_VESA_Bus;
+	HostAdapter->DMA_Channel = 0;
+	break;
+      case '5':
+	HostAdapter->BusType = BusLogic_ISA_Bus;
+	break;
+      case '6':
+	HostAdapter->BusType = BusLogic_MCA_Bus;
+	HostAdapter->DMA_Channel = 0;
+	break;
+      case '7':
+	HostAdapter->BusType = BusLogic_EISA_Bus;
+	HostAdapter->DMA_Channel = 0;
+	break;
+      case '9':
+	HostAdapter->BusType = BusLogic_PCI_Bus;
+	HostAdapter->DMA_Channel = 0;
+	break;
+      }
+  /*
+    Determine whether Extended Translation is enabled and save it in
+    the Host Adapter structure.
+  */
+  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+  if (GeometryRegister & BusLogic_ExtendedTranslationEnabled)
+    HostAdapter->ExtendedTranslation = true;
+  /*
+    Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter
+    structure.  The Disconnect Permitted information is only valid on "C"
+    Series boards, but Disconnect/Reconnect is always permitted on "S" and
+    "A" Series boards.
+  */
+  if (HostAdapter->FirmwareVersion[0] >= '4')
+    HostAdapter->DisconnectPermitted =
+      (SetupInformation.DisconnectPermittedID8to15 << 8)
+      | SetupInformation.DisconnectPermittedID0to7;
+  else HostAdapter->DisconnectPermitted = 0xFF;
+  /*
+    Save the Scatter Gather Limits, Level Triggered Interrupts flag,
+    Wide SCSI flag, and Differential SCSI flag in the Host Adapter structure.
+  */
+  HostAdapter->HostAdapterScatterGatherLimit =
+    ExtendedSetupInformation.ScatterGatherLimit;
+  HostAdapter->DriverScatterGatherLimit =
+    HostAdapter->HostAdapterScatterGatherLimit;
+  if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
+    HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+  if (ExtendedSetupInformation.Misc.LevelTriggeredInterrupts)
+    HostAdapter->LevelTriggeredInterrupts = true;
+  if (ExtendedSetupInformation.HostWideSCSI)
+    {
+      HostAdapter->HostWideSCSI = true;
+      HostAdapter->MaxTargetIDs = 16;
+      HostAdapter->MaxLogicalUnits = 64;
+    }
+  else
+    {
+      HostAdapter->HostWideSCSI = false;
+      HostAdapter->MaxTargetIDs = 8;
+      HostAdapter->MaxLogicalUnits = 8;
+    }
+  HostAdapter->HostDifferentialSCSI =
+    ExtendedSetupInformation.HostDifferentialSCSI;
+  /*
+    Determine the Host Adapter BIOS Address if the BIOS is enabled and
+    save it in the Host Adapter structure.  The BIOS is disabled if the
+    BIOS_Address is 0.
+  */
+  HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+  /*
+    Select an appropriate value for Concurrency (Commands per Logical Unit)
+    either from a Command Line Entry, or based on whether this is an ISA
+    or non-ISA Host Adapter.
+  */
+  if (HostAdapter->CommandLineEntry != NULL &&
+      HostAdapter->CommandLineEntry->Concurrency > 0)
+    HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency;
+  else if (HostAdapter->BusType == BusLogic_ISA_Bus)
+    HostAdapter->Concurrency = BusLogic_Concurrency_ISA;
+  else HostAdapter->Concurrency = BusLogic_Concurrency;
+  /*
+    Select an appropriate value for Bus Settle Time either from a Command
+    Line Entry, or from BusLogic_DefaultBusSettleTime.
+  */
+  if (HostAdapter->CommandLineEntry != NULL &&
+      HostAdapter->CommandLineEntry->BusSettleTime > 0)
+    HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime;
+  else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+  /*
+    Select appropriate values for the Error Recovery Option array either from
+    a Command Line Entry, or using BusLogic_ErrorRecoveryDefault.
+  */
+  if (HostAdapter->CommandLineEntry != NULL)
+    memcpy(HostAdapter->ErrorRecoveryOption,
+	   HostAdapter->CommandLineEntry->ErrorRecoveryOption,
+	   sizeof(HostAdapter->ErrorRecoveryOption));
+  else memset(HostAdapter->ErrorRecoveryOption,
+	      BusLogic_ErrorRecoveryDefault,
+	      sizeof(HostAdapter->ErrorRecoveryOption));
+  /*
+    Tagged Queuing support is available and operates properly only on "C"
+    Series boards with firmware version 4.22 and above and on "S" Series
+    boards with firmware version 3.35 and above.  Tagged Queuing is disabled
+    by default when the Concurrency value is 1 since queuing multiple commands
+    is not possible.
+  */
+  TaggedQueuingPermittedDefault = 0;
+  if (HostAdapter->Concurrency > 1)
+    switch (HostAdapter->FirmwareVersion[0])
+      {
+      case '4':
+	if (HostAdapter->FirmwareVersion[2] > '2' ||
+	    (HostAdapter->FirmwareVersion[2] == '2' &&
+	     HostAdapter->FirmwareVersion[3] >= '2'))
+	  TaggedQueuingPermittedDefault = 0xFFFF;
+	break;
+      case '3':
+	if (HostAdapter->FirmwareVersion[2] > '3' ||
+	    (HostAdapter->FirmwareVersion[2] == '3' &&
+	     HostAdapter->FirmwareVersion[3] >= '5'))
+	  TaggedQueuingPermittedDefault = 0xFFFF;
+	break;
+      }
+  /*
+    Combine the default Tagged Queuing permission based on the Host Adapter
+    firmware version and Concurrency with any Command Line Entry Tagged
+    Queuing specification.
+  */
+  if (HostAdapter->CommandLineEntry != NULL)
+    HostAdapter->TaggedQueuingPermitted =
+      (HostAdapter->CommandLineEntry->TaggedQueuingPermitted &
+       HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) |
+      (TaggedQueuingPermittedDefault &
+       ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask);
+  else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault;
+  /*
+    Announce the Host Adapter Configuration.
+  */
+  printk("scsi%d: Configuring BusLogic Model %s %s%s%s SCSI Host Adapter\n",
+	 HostAdapter->HostNumber, HostAdapter->ModelName,
+	 BusLogic_BusNames[HostAdapter->BusType],
+	 (HostAdapter->HostWideSCSI ? " Wide" : ""),
+	 (HostAdapter->HostDifferentialSCSI ? " Differential" : ""));
+  printk("scsi%d:   Firmware Version: %s, I/O Address: 0x%X, "
+	 "IRQ Channel: %d/%s\n",
+	 HostAdapter->HostNumber, HostAdapter->FirmwareVersion,
+	 HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
+	 (HostAdapter->LevelTriggeredInterrupts ? "Level" : "Edge"));
+  printk("scsi%d:   DMA Channel: ", HostAdapter->HostNumber);
+  if (HostAdapter->DMA_Channel > 0)
+    printk("%d, ", HostAdapter->DMA_Channel);
+  else printk("None, ");
+  if (HostAdapter->BIOS_Address > 0)
+    printk("BIOS Address: 0x%lX, ", HostAdapter->BIOS_Address);
+  else printk("BIOS Address: None, ");
+  printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID);
+  printk("scsi%d:   Scatter/Gather Limit: %d segments, "
+	 "Synchronous Initiation: %s\n", HostAdapter->HostNumber,
+	 HostAdapter->HostAdapterScatterGatherLimit,
+	 (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"));
+  printk("scsi%d:   SCSI Parity Checking: %s, "
+	 "Extended Disk Translation: %s\n", HostAdapter->HostNumber,
+	 (HostAdapter->ParityChecking ? "Enabled" : "Disabled"),
+	 (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled"));
+  AllTargetsMask = (1 << HostAdapter->MaxTargetIDs) - 1;
+  DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
+  printk("scsi%d:   Disconnect/Reconnect: ", HostAdapter->HostNumber);
+  if (DisconnectPermitted == 0)
+    printk("Disabled");
+  else if (DisconnectPermitted == AllTargetsMask)
+    printk("Enabled");
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+  printk(", Tagged Queuing: ");
+  TaggedQueuingPermitted =
+    HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
+  if (TaggedQueuingPermitted == 0)
+    printk("Disabled");
+  else if (TaggedQueuingPermitted == AllTargetsMask)
+    printk("Enabled");
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+  printk("\n");
+  CommonErrorRecovery = true;
+  for (TargetID = 1; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+    if (HostAdapter->ErrorRecoveryOption[TargetID] !=
+	HostAdapter->ErrorRecoveryOption[0])
+      {
+	CommonErrorRecovery = false;
+	break;
+      }
+  printk("scsi%d:   Error Recovery: ", HostAdapter->HostNumber);
+  if (CommonErrorRecovery)
+    printk("%s", BusLogic_ErrorRecoveryOptions[
+		   HostAdapter->ErrorRecoveryOption[0]]);
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%s", BusLogic_ErrorRecoveryOptions2[
+		     HostAdapter->ErrorRecoveryOption[TargetID]]);
+  printk(", Mailboxes: %d, Initial CCBs: %d\n",
+	 BusLogic_MailboxCount, BusLogic_InitialCCBs);
+  printk("scsi%d:   Driver Scatter/Gather Limit: %d segments, "
+	 "Concurrency: %d\n", HostAdapter->HostNumber,
+	 HostAdapter->DriverScatterGatherLimit, HostAdapter->Concurrency);
+  /*
+    Indicate reading the Host Adapter Configuration completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_AcquireResources acquires the system resources necessary to use Host
+  Adapter, and initializes the fields in the SCSI Host structure.  The base,
+  io_port, n_io_ports, irq, and dma_channel fields in the SCSI Host structure
+  are intentionally left uninitialized, as this driver handles acquisition and
+  release of these resources explicitly, as well as ensuring exclusive access
+  to the Host Adapter hardware and data structures through explicit locking.
+*/
+
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter,
+					 SCSI_Host_T *Host)
+{
+  /*
+    Acquire exclusive or shared access to the IRQ Channel.  A usage count is
+    maintained so that PCI, EISA, or MCA shared Interrupts can be supported.
+  */
+  if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]++ == 0)
+    {
+      if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+		      SA_INTERRUPT, HostAdapter->InterruptLabel) < 0)
+	{
+	  BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--;
+	  printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+		 HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+	  return false;
+	}
+    }
+  else
+    {
+      BusLogic_HostAdapter_T *FirstHostAdapter =
+	BusLogic_RegisteredHostAdapters;
+      while (FirstHostAdapter != NULL)
+	{
+	  if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel)
+	    {
+	      if (strlen(FirstHostAdapter->InterruptLabel) + 8
+		  < sizeof(FirstHostAdapter->InterruptLabel))
+		{
+		  strcat(FirstHostAdapter->InterruptLabel, " + ");
+		  strcat(FirstHostAdapter->InterruptLabel,
+			 HostAdapter->ModelName);
+		}
+	      break;
+	    }
+	  FirstHostAdapter = FirstHostAdapter->Next;
+	}
+    }
+  HostAdapter->IRQ_ChannelAcquired = true;
+  /*
+    Acquire exclusive access to the DMA Channel.
+  */
+  if (HostAdapter->DMA_Channel > 0)
+    {
+      if (request_dma(HostAdapter->DMA_Channel, HostAdapter->BoardName) < 0)
+	{
+	  printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
+		 HostAdapter->HostNumber, HostAdapter->DMA_Channel);
+	  return false;
+	}
+      set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
+      enable_dma(HostAdapter->DMA_Channel);
+      HostAdapter->DMA_ChannelAcquired = true;
+    }
+  /*
+    Initialize necessary fields in the SCSI Host structure.
+  */
+  Host->max_id = HostAdapter->MaxTargetIDs;
+  Host->max_lun = HostAdapter->MaxLogicalUnits;
+  Host->max_channel = 0;
+  Host->this_id = HostAdapter->SCSI_ID;
+  Host->can_queue = BusLogic_MailboxCount;
+  Host->cmd_per_lun = HostAdapter->Concurrency;
+  Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+  Host->unchecked_isa_dma = (HostAdapter->BusType == BusLogic_ISA_Bus);
+  /*
+    BusLogic 445S Host Adapters prior to board revision D have a hardware bug
+    whereby when the BIOS is enabled, transfers to/from the same address range
+    the BIOS occupies modulo 16MB are handled incorrectly.  Since 16KB out of
+    each 16MB after the first is such a small amount of memory, this memory
+    can be marked as reserved without a significant loss of performance; this
+    is a much cheaper solution than requiring that ISA bounce buffers be used.
+  */
+  if (HostAdapter->BIOS_Address > 0 &&
+      strcmp(HostAdapter->ModelName, "445S") == 0)
+    {
+      Host->forbidden_addr = HostAdapter->BIOS_Address;
+      Host->forbidden_size = 16*1024;
+    }
+  /*
+    Indicate the System Resource Acquisition completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_ReleaseResources releases any system resources previously acquired
+  by BusLogic_AcquireResources.
+*/
+
+static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
+{
+  /*
+    Release exclusive or shared access to the IRQ Channel.
+  */
+  if (HostAdapter->IRQ_ChannelAcquired)
+    if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9] == 0)
+      free_irq(HostAdapter->IRQ_Channel);
+  /*
+    Release exclusive access to the DMA Channel.
+  */
+  if (HostAdapter->DMA_ChannelAcquired)
+    free_dma(HostAdapter->DMA_Channel);
+}
+
+
+/*
+  BusLogic_TestInterrupts tests for proper functioning of the Host Adapter
+  Interrupt Register and that interrupts generated by the Host Adapter are
+  getting through to the Interrupt Handler.  A large proportion of initial
+  problems with installing PCI Host Adapters are due to configuration problems
+  where either the Host Adapter or Motherboard is configured incorrectly, and
+  interrupts do not get through as a result.
+*/
+
+static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
+{
+  unsigned int InitialInterruptCount, FinalInterruptCount;
+  int TestCount = 5, i;
+  InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+  /*
+    Issue the Test Command Complete Interrupt commands.
+  */
+  for (i = 0; i < TestCount; i++)
+    BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt,
+		     NULL, 0, NULL, 0);
+  /*
+    Verify that BusLogic_InterruptHandler was called at least TestCount times.
+    Shared IRQ Channels could cause more than TestCount interrupts to occur,
+    but there should never be fewer than TestCount.
+  */
+  FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+  if (FinalInterruptCount < InitialInterruptCount + TestCount)
+    {
+      printk("scsi%d: HOST ADAPTER INTERRUPT TEST FAILED - DETACHING\n",
+	     HostAdapter->HostNumber);
+      printk("scsi%d:  Interrupts are not getting through "
+	     "from the Host Adapter to the\n", HostAdapter->HostNumber);
+      printk("scsi%d:  BusLogic Driver Interrupt Handler.  "
+	     "The most likely cause is that\n", HostAdapter->HostNumber);
+      printk("scsi%d:  either the Host Adapter or Motherboard "
+	     "is configured incorrectly.\n", HostAdapter->HostNumber);
+      printk("scsi%d:  Please check the Host Adapter configuration "
+	     "with AutoSCSI or by\n", HostAdapter->HostNumber);
+      printk("scsi%d:  examining any dip switch and jumper settings "
+	     "on the Host Adapter, and\n", HostAdapter->HostNumber);
+      printk("scsi%d:  verify that no other device is attempting to "
+	     "use the same IRQ Channel.\n", HostAdapter->HostNumber);
+      printk("scsi%d:  For PCI Host Adapters, it may also be necessary "
+	     "to investigate and\n", HostAdapter->HostNumber);
+      printk("scsi%d:  manually set the PCI interrupt assignments "
+	     "and edge/level interrupt\n", HostAdapter->HostNumber);
+      printk("scsi%d:  type selection in the BIOS Setup Program or "
+	     "with Motherboard jumpers.\n", HostAdapter->HostNumber);
+      return false;
+    }
+  /*
+    Indicate the Host Adapter Interrupt Test completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_InitializeHostAdapter initializes Host Adapter.  This is the only
+  function called during SCSI Host Adapter detection which modifies the state
+  of the Host Adapter from its initial power on or hard reset state.
+*/
+
+static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
+					      *HostAdapter)
+{
+  BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest;
+  BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest;
+  BusLogic_WideModeCCBRequest_T WideModeCCBRequest;
+  BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+  /*
+    Initialize Read/Write Operation Count and Command Successful Flag
+    for each Target.
+  */
+  memset(HostAdapter->ReadWriteOperationCount, 0,
+	 sizeof(HostAdapter->ReadWriteOperationCount));
+  memset(HostAdapter->CommandSuccessfulFlag, false,
+	 sizeof(HostAdapter->CommandSuccessfulFlag));
+  /*
+    Initialize the Outgoing and Incoming Mailbox structures.
+  */
+  memset(HostAdapter->OutgoingMailboxes, 0,
+	 sizeof(HostAdapter->OutgoingMailboxes));
+  memset(HostAdapter->IncomingMailboxes, 0,
+	 sizeof(HostAdapter->IncomingMailboxes));
+  /*
+    Initialize the pointers to the First, Last, and Next Mailboxes.
+  */
+  HostAdapter->FirstOutgoingMailbox = &HostAdapter->OutgoingMailboxes[0];
+  HostAdapter->LastOutgoingMailbox =
+    &HostAdapter->OutgoingMailboxes[BusLogic_MailboxCount-1];
+  HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+  HostAdapter->FirstIncomingMailbox = &HostAdapter->IncomingMailboxes[0];
+  HostAdapter->LastIncomingMailbox =
+    &HostAdapter->IncomingMailboxes[BusLogic_MailboxCount-1];
+  HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+  /*
+    Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+  */
+  ExtendedMailboxRequest.MailboxCount = BusLogic_MailboxCount;
+  ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->OutgoingMailboxes;
+  if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
+		       &ExtendedMailboxRequest,
+		       sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+    {
+      printk("scsi%d: MAILBOX INITIALIZATION FAILED - DETACHING\n",
+	     HostAdapter->HostNumber);
+      return false;
+    }
+  /*
+    Enable Strict Round Robin Mode if supported by the Host Adapter.  In Strict
+    Round Robin Mode, the Host Adapter only looks at the next Outgoing Mailbox
+    for each new command, rather than scanning through all the Outgoing
+    Mailboxes to find any that have new commands in them.  BusLogic indicates
+    that Strict Round Robin Mode is significantly more efficient.
+  */
+  RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+  BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+		   &RoundRobinModeRequest,
+		   sizeof(RoundRobinModeRequest), NULL, 0);
+  /*
+    For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to
+    allow more than 8 Logical Units per Target to be supported.
+  */
+  if (HostAdapter->HostWideSCSI)
+    {
+      WideModeCCBRequest = BusLogic_WideModeCCB;
+      if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB,
+			   &WideModeCCBRequest,
+			   sizeof(WideModeCCBRequest), NULL, 0) < 0)
+	{
+	  printk("scsi%d: ENABLE WIDE MODE CCB FAILED - DETACHING\n",
+		 HostAdapter->HostNumber);
+	  return false;
+	}
+    }
+  /*
+    For PCI Host Adapters being accessed through the PCI compliant I/O
+    Address, disable the ISA compatible I/O Address to avoid detecting the
+    same Host Adapter at both I/O Addresses.
+  */
+  if (HostAdapter->BusType == BusLogic_PCI_Bus)
+    {
+      int Index;
+      for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++)
+	if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index])
+	  break;
+      if (BusLogic_IO_StandardAddresses[Index] == 0)
+	{
+	  ModifyIOAddressRequest = BusLogic_ModifyIO_Disable;
+	  if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+			       &ModifyIOAddressRequest,
+			       sizeof(ModifyIOAddressRequest), NULL, 0) < 0)
+	    {
+	      printk("scsi%d: MODIFY I/O ADDRESS FAILED - DETACHING\n",
+		     HostAdapter->HostNumber);
+	      return false;
+	    }
+	}
+    }
+  /*
+    Announce Successful Initialization.
+  */
+  printk("scsi%d: *** %s Initialized Successfully ***\n",
+	 HostAdapter->HostNumber, HostAdapter->BoardName);
+  /*
+    Indicate the Host Adapter Initialization completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_InquireTargetDevices inquires about the Target Devices accessible
+  through Host Adapter and reports on the results.
+*/
+
+static boolean BusLogic_InquireTargetDevices(BusLogic_HostAdapter_T
+					     *HostAdapter)
+{
+  BusLogic_InstalledDevices8_T InstalledDevicesID0to7;
+  BusLogic_InstalledDevices8_T InstalledDevicesID8to15;
+  BusLogic_SetupInformation_T SetupInformation;
+  BusLogic_SynchronousPeriod_T SynchronousPeriod;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  int TargetDevicesFound = 0, TargetID;
+  /*
+    Wait a few seconds between the Host Adapter Hard Reset which initiates
+    a SCSI Bus Reset and issuing any SCSI commands.  Some SCSI devices get
+    confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+  */
+  BusLogic_Delay(HostAdapter->BusSettleTime);
+  /*
+    Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI
+    Host Adapters the Inquire Installed Devices ID 8 to 15 command.  This is
+    necessary to force Synchronous Transfer Negotiation so that the Inquire
+    Setup Information and Inquire Synchronous Period commands will return
+    valid data.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7,
+		       NULL, 0, &InstalledDevicesID0to7,
+		       sizeof(InstalledDevicesID0to7))
+      != sizeof(InstalledDevicesID0to7))
+    return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+  if (HostAdapter->HostWideSCSI)
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID8to15,
+			 NULL, 0, &InstalledDevicesID8to15,
+			 sizeof(InstalledDevicesID8to15))
+	!= sizeof(InstalledDevicesID8to15))
+      return BusLogic_Failure(HostAdapter,
+			      "INQUIRE INSTALLED DEVICES ID 8 TO 15");
+  /*
+    Issue the Inquire Setup Information command.
+  */
+  RequestedReplyLength = sizeof(SetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+		       &RequestedReplyLength, sizeof(RequestedReplyLength),
+		       &SetupInformation, sizeof(SetupInformation))
+      != sizeof(SetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+  /*
+    Issue the Inquire Synchronous Period command.
+  */
+  RequestedReplyLength = sizeof(SynchronousPeriod);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+		       &RequestedReplyLength, 1,
+		       &SynchronousPeriod, sizeof(SynchronousPeriod))
+      != sizeof(SynchronousPeriod))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+  /*
+    Save the Installed Devices, Synchronous Values, and Synchronous Period
+    information in the Host Adapter structure.
+  */
+  memcpy(HostAdapter->InstalledDevices, InstalledDevicesID0to7,
+	 sizeof(BusLogic_InstalledDevices8_T));
+  memcpy(HostAdapter->SynchronousValues,
+	 SetupInformation.SynchronousValuesID0to7,
+	 sizeof(BusLogic_SynchronousValues8_T));
+  if (HostAdapter->HostWideSCSI)
+    {
+      memcpy(&HostAdapter->InstalledDevices[8], InstalledDevicesID8to15,
+	     sizeof(BusLogic_InstalledDevices8_T));
+      memcpy(&HostAdapter->SynchronousValues[8],
+	     SetupInformation.SynchronousValuesID8to15,
+	     sizeof(BusLogic_SynchronousValues8_T));
+    }
+  memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod,
+	 sizeof(BusLogic_SynchronousPeriod_T));
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+    if (HostAdapter->InstalledDevices[TargetID] != 0)
+      {
+	int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID];
+	if (SynchronousPeriod > 10)
+	  {
+	    int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+	    int RoundedSynchronousTransferRate =
+	      (SynchronousTransferRate + 5000) / 10000;
+	    printk("scsi%d:   Target %d: Synchronous at "
+		   "%d.%02d mega-transfers/sec, offset %d\n",
+		   HostAdapter->HostNumber, TargetID,
+		   RoundedSynchronousTransferRate / 100,
+		   RoundedSynchronousTransferRate % 100,
+		   HostAdapter->SynchronousValues[TargetID].Offset);
+	  }
+	else if (SynchronousPeriod > 0)
+	  {
+	    int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+	    int RoundedSynchronousTransferRate =
+	      (SynchronousTransferRate + 50000) / 100000;
+	    printk("scsi%d:   Target %d: Synchronous at "
+		   "%d.%01d mega-transfers/sec, offset %d\n",
+		   HostAdapter->HostNumber, TargetID,
+		   RoundedSynchronousTransferRate / 10,
+		   RoundedSynchronousTransferRate % 10,
+		   HostAdapter->SynchronousValues[TargetID].Offset);
+	  }
+	else printk("scsi%d:   Target %d: Asynchronous\n",
+		    HostAdapter->HostNumber, TargetID);
+	TargetDevicesFound++;
+      }
+  if (TargetDevicesFound == 0)
+    printk("scsi%d:   No Target Devices Found\n", HostAdapter->HostNumber);
+  /*
+    Indicate the Target Device Inquiry completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+  I/O Addresses where they may be located, initializing, registering, and
+  reporting the configuration of each BusLogic Host Adapter it finds.  It
+  returns the number of BusLogic Host Adapters successfully initialized and
+  registered.
+*/
+
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
+{
+  int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0;
+  int AddressProbeIndex = 0;
+  BusLogic_InitializeAddressProbeList();
+  while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0)
+    {
+      BusLogic_HostAdapter_T HostAdapterPrototype;
+      BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
+      SCSI_Host_T *Host;
+      memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
+      HostAdapter->IO_Address =
+	BusLogic_IO_AddressProbeList[AddressProbeIndex++];
+      /*
+	Initialize the Command Line Entry field if an explicit I/O Address
+	was specified.
+      */
+      if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+	  BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address ==
+	  HostAdapter->IO_Address)
+	HostAdapter->CommandLineEntry =
+	  &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+      /*
+	Check whether the I/O Address range is already in use.
+      */
+      if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0)
+	continue;
+      /*
+	Probe the Host Adapter.  If unsuccessful, abort further initialization.
+      */
+      if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue;
+      /*
+	Hard Reset the Host Adapter.  If unsuccessful, abort further
+	initialization.
+      */
+      if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue;
+      /*
+	Check the Host Adapter.  If unsuccessful, abort further initialization.
+      */
+      if (!BusLogic_CheckHostAdapter(HostAdapter)) continue;
+      /*
+	Initialize the Command Line Entry field if an explicit I/O Address
+	was not specified.
+      */
+      if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+	  BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0)
+	HostAdapter->CommandLineEntry =
+	  &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+      /*
+	Announce the Driver Version and Date, Author's Name, Copyright Notice,
+	and Contact Address.
+      */
+      BusLogic_AnnounceDriver();
+      /*
+	Register usage of the I/O Address range.  From this point onward, any
+	failure will be assumed to be due to a problem with the Host Adapter,
+	rather than due to having mistakenly identified this port as belonging
+	to a BusLogic Host Adapter.  The I/O Address range will not be
+	released, thereby preventing it from being incorrectly identified as
+	any other type of Host Adapter.
+      */
+      request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+		     "BusLogic");
+      /*
+	Register the SCSI Host structure.
+      */
+      HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry;
+      Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T));
+      HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata;
+      memcpy(HostAdapter, &HostAdapterPrototype,
+	     sizeof(BusLogic_HostAdapter_T));
+      HostAdapter->SCSI_Host = Host;
+      HostAdapter->HostNumber = Host->host_no;
+      /*
+	Add Host Adapter to the end of the list of registered BusLogic
+	Host Adapters.  In order for Command Complete Interrupts to be
+	properly dismissed by BusLogic_InterruptHandler, the Host Adapter
+	must be registered.  This must be done before the IRQ Channel is
+	acquired, and in a shared IRQ Channel environment, must be done
+	before any Command Complete Interrupts occur, since the IRQ Channel
+	may have already been acquired by a previous BusLogic Host Adapter.
+      */
+      BusLogic_RegisterHostAdapter(HostAdapter);
+      /*
+	Read the Host Adapter Configuration, Acquire the System Resources
+	necessary to use Host Adapter and initialize the fields in the SCSI
+	Host structure, then Test Interrupts, Create the CCBs, and finally
+	Initialize the Host Adapter.
+      */
+      if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+	  BusLogic_AcquireResources(HostAdapter, Host) &&
+	  BusLogic_TestInterrupts(HostAdapter) &&
+	  BusLogic_CreateCCBs(HostAdapter) &&
+	  BusLogic_InitializeHostAdapter(HostAdapter) &&
+	  BusLogic_InquireTargetDevices(HostAdapter))
+	{
+	  /*
+	    Initialization has been completed successfully.  Release and
+	    re-register usage of the I/O Address range so that the Model
+	    Name of the Host Adapter will appear.
+	  */
+	  release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+	  request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+			 HostAdapter->BoardName);
+	  BusLogicHostAdapterCount++;
+	}
+      else
+	{
+	  /*
+	    An error occurred during Host Adapter Configuration Querying,
+	    Resource Acquisition, Interrupt Testing, CCB Creation, or Host
+	    Adapter Initialization, so remove Host Adapter from the list of
+	    registered BusLogic Host Adapters, destroy the CCBs, Release
+	    the System Resources, and Unregister the SCSI Host.
+	  */
+	  BusLogic_DestroyCCBs(HostAdapter);
+	  BusLogic_ReleaseResources(HostAdapter);
+	  BusLogic_UnregisterHostAdapter(HostAdapter);
+	  scsi_unregister(Host);
+	}
+    }
+  return BusLogicHostAdapterCount;
+}
+
+
+/*
+  BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+  support a specific Host Adapter, including the I/O Address range, and
+  unregisters the BusLogic Host Adapter.
+*/
+
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Host->hostdata;
+  /*
+    Destroy the CCBs and release any system resources acquired to use
+    Host Adapter.
+  */
+  BusLogic_DestroyCCBs(HostAdapter);
+  BusLogic_ReleaseResources(HostAdapter);
+  /*
+    Release usage of the I/O Address range.
+  */
+  release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+  /*
+    Remove Host Adapter from the list of registered BusLogic Host Adapters.
+  */
+  BusLogic_UnregisterHostAdapter(HostAdapter);
+  return 0;
+}
+
+
+/*
+  BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+  the Host Adapter Status and Target Device Status.
+*/
+
+static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
+					HostAdapterStatus,
+				      BusLogic_TargetDeviceStatus_T
+					TargetDeviceStatus)
+{
+  int HostStatus;
+  switch (HostAdapterStatus)
+    {
+    case BusLogic_CommandCompletedNormally:
+    case BusLogic_LinkedCommandCompleted:
+    case BusLogic_LinkedCommandCompletedWithFlag:
+      HostStatus = DID_OK;
+      break;
+    case BusLogic_SCSISelectionTimeout:
+      HostStatus = DID_TIME_OUT;
+      break;
+    case BusLogic_InvalidOutgoingMailboxActionCode:
+    case BusLogic_InvalidCommandOperationCode:
+    case BusLogic_InvalidCommandParameter:
+      printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n",
+	     HostAdapterStatus);
+    case BusLogic_DataOverUnderRun:
+    case BusLogic_UnexpectedBusFree:
+    case BusLogic_LinkedCCBhasInvalidLUN:
+    case BusLogic_AutoRequestSenseFailed:
+    case BusLogic_TaggedQueuingMessageRejected:
+    case BusLogic_UnsupportedMessageReceived:
+    case BusLogic_HostAdapterHardwareFailed:
+    case BusLogic_TargetDeviceReconnectedImproperly:
+    case BusLogic_AbortQueueGenerated:
+    case BusLogic_HostAdapterSoftwareError:
+    case BusLogic_HostAdapterHardwareTimeoutError:
+    case BusLogic_SCSIParityErrorDetected:
+      HostStatus = DID_ERROR;
+      break;
+    case BusLogic_InvalidBusPhaseRequested:
+    case BusLogic_TargetFailedResponseToATN:
+    case BusLogic_HostAdapterAssertedRST:
+    case BusLogic_OtherDeviceAssertedRST:
+    case BusLogic_HostAdapterAssertedBusDeviceReset:
+      HostStatus = DID_RESET;
+      break;
+    default:
+      printk("BusLogic: unknown Host Adapter Status 0x%02X\n",
+	     HostAdapterStatus);
+      HostStatus = DID_ERROR;
+      break;
+    }
+  return (HostStatus << 16) | TargetDeviceStatus;
+}
+
+
+/*
+  BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+  Adapters.  To simplify handling shared IRQ Channels, all installed BusLogic
+  Host Adapters are scanned whenever any one of them signals a hardware
+  interrupt.
+*/
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
+				      Registers_T *InterruptRegisters)
+{
+  BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL;
+  BusLogic_HostAdapter_T *HostAdapter;
+  int HostAdapterResetPendingCount = 0;
+  /*
+    Iterate over the installed BusLogic Host Adapters accepting any Incoming
+    Mailbox entries and saving the completed CCBs for processing.  This
+    interrupt handler is installed with SA_INTERRUPT, so interrupts are
+    disabled when the interrupt handler is entered.
+  */
+  for (HostAdapter = BusLogic_RegisteredHostAdapters;
+       HostAdapter != NULL;
+       HostAdapter = HostAdapter->Next)
+    {
+      unsigned char InterruptRegister;
+      /*
+	Acquire exclusive access to Host Adapter.
+      */
+      BusLogic_LockHostAdapterID(HostAdapter);
+      /*
+	Read the Host Adapter Interrupt Register.
+      */
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_InterruptValid)
+	{
+	  /*
+	    Acknowledge the interrupt and reset the Host Adapter
+	    Interrupt Register.
+	  */
+	  BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+	  /*
+	    Process valid SCSI Reset State and Incoming Mailbox Loaded
+	    interrupts.  Command Complete interrupts are noted, and
+	    Outgoing Mailbox Available interrupts are ignored, as they
+	    are never enabled.
+	  */
+	  if (InterruptRegister & BusLogic_SCSIResetState)
+	    {
+	      HostAdapter->HostAdapterResetPending = true;
+	      HostAdapterResetPendingCount++;
+	    }
+	  else if (InterruptRegister & BusLogic_IncomingMailboxLoaded)
+	    {
+	      /*
+		Scan through the Incoming Mailboxes in Strict Round Robin
+		fashion, saving any completed CCBs for further processing.
+		It is essential that for each CCB and SCSI Command issued,
+		completion processing is performed exactly once.  Therefore,
+		only Incoming Mailbox entries with completion code Command
+		Completed Without Error, Command Completed With Error, or
+		Command Aborted At Host Request are saved for completion
+		processing.  When an Incoming Mailbox entry has a completion
+		code of Aborted Command Not Found, the CCB had already
+		completed or been aborted before the current Abort request
+		was processed, and so completion processing has already
+		occurred and no further action should be taken.
+	      */
+	      BusLogic_IncomingMailbox_T *NextIncomingMailbox =
+		HostAdapter->NextIncomingMailbox;
+	      while (NextIncomingMailbox->CompletionCode !=
+		     BusLogic_IncomingMailboxFree)
+		{
+		  BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
+		  BusLogic_CompletionCode_T MailboxCompletionCode =
+		    NextIncomingMailbox->CompletionCode;
+		  if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
+		    {
+		      /*
+			Mark this CCB as completed and add it to the end
+			of the list of completed CCBs.
+		      */
+		      CCB->Status = BusLogic_CCB_Completed;
+		      CCB->MailboxCompletionCode = MailboxCompletionCode;
+		      CCB->Next = NULL;
+		      if (FirstCompletedCCB == NULL)
+			{
+			  FirstCompletedCCB = CCB;
+			  LastCompletedCCB = CCB;
+			}
+		      else
+			{
+			  LastCompletedCCB->Next = CCB;
+			  LastCompletedCCB = CCB;
+			}
+		    }
+		  else printk("scsi%d: Aborted CCB #%d Not Found\n",
+			      HostAdapter->HostNumber, CCB->SerialNumber);
+		  NextIncomingMailbox->CompletionCode =
+		    BusLogic_IncomingMailboxFree;
+		  if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+		    NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+		}
+	      HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+	    }
+	  else if (InterruptRegister & BusLogic_CommandComplete)
+	    HostAdapter->HostAdapterCommandCompleted = true;
+	}
+      /*
+	Release exclusive access to Host Adapter.
+      */
+      BusLogic_UnlockHostAdapterID(HostAdapter);
+    }
+  /*
+    Enable interrupts while the completed CCBs are processed.
+  */
+  sti();
+  /*
+    Iterate over the Host Adapters performing any pending Host Adapter Resets.
+  */
+  if (HostAdapterResetPendingCount > 0)
+    for (HostAdapter = BusLogic_RegisteredHostAdapters;
+	 HostAdapter != NULL;
+	 HostAdapter = HostAdapter->Next)
+      if (HostAdapter->HostAdapterResetPending)
+	{
+	  BusLogic_ResetHostAdapter(HostAdapter, NULL);
+	  HostAdapter->HostAdapterResetPending = false;
+	  scsi_mark_host_bus_reset(HostAdapter->SCSI_Host);
+	}
+  /*
+    Iterate over the completed CCBs setting the SCSI Command Result Codes,
+    deallocating the CCBs, and calling the Completion Routines.
+  */
+  while (FirstCompletedCCB != NULL)
+    {
+      BusLogic_CCB_T *CCB = FirstCompletedCCB;
+      SCSI_Command_T *Command = CCB->Command;
+      FirstCompletedCCB = FirstCompletedCCB->Next;
+      HostAdapter = CCB->HostAdapter;
+      /*
+	Bus Device Reset CCBs have the Command field non-NULL only when a Bus
+	Device Reset was requested for a command that was not currently active
+	in the Host Adapter, and hence would not have its Completion Routine
+	called otherwise.
+      */
+      if (CCB->Opcode == BusLogic_SCSIBusDeviceReset)
+	{
+	  printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n",
+		 HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+	  if (Command != NULL) Command->result = DID_RESET << 16;
+	}
+      else
+	/*
+	  Translate the Mailbox Completion Code, Host Adapter Status, and
+	  Target Device Status into a SCSI Subsystem Result Code.
+	*/
+	switch (CCB->MailboxCompletionCode)
+	  {
+	  case BusLogic_IncomingMailboxFree:
+	  case BusLogic_AbortedCommandNotFound:
+	    printk("scsi%d: CCB #%d Impossible State\n",
+		   HostAdapter->HostNumber, CCB->SerialNumber);
+	    break;
+	  case BusLogic_CommandCompletedWithoutError:
+	    HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
+	    Command->result = DID_OK << 16;
+	    break;
+	  case BusLogic_CommandAbortedAtHostRequest:
+	    printk("scsi%d: CCB #%d Aborted\n",
+		   HostAdapter->HostNumber, CCB->SerialNumber);
+	    Command->result = DID_ABORT << 16;
+	    break;
+	  case BusLogic_CommandCompletedWithError:
+	    Command->result =
+	      BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
+					 CCB->TargetDeviceStatus);
+	    if (BusLogic_TracingOptions & BusLogic_TraceErrors)
+	      if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+		{
+		  int i;
+		  printk("scsi%d: CCB #%d Target %d: Result %X "
+			 "Host Adapter Status %02X Target Status %02X\n",
+			 HostAdapter->HostNumber, CCB->SerialNumber,
+			 CCB->TargetID, Command->result,
+			 CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
+		  printk("scsi%d: CDB   ", HostAdapter->HostNumber);
+		  for (i = 0; i < CCB->CDB_Length; i++)
+		    printk(" %02X", CCB->CDB[i]);
+		  printk("\n");
+		  printk("scsi%d: Sense ", HostAdapter->HostNumber);
+		  for (i = 0; i < CCB->SenseDataLength; i++)
+		    printk(" %02X", (*CCB->SenseDataPointer)[i]);
+		  printk("\n");
+		}
+	    break;
+	  }
+      /*
+	Place CCB back on the Host Adapter's free list.
+      */
+      BusLogic_DeallocateCCB(CCB);
+      /*
+	Call the SCSI Command Completion Routine if appropriate.
+      */
+      if (Command != NULL) Command->scsi_done(Command);
+    }
+}
+
+
+/*
+  BusLogic_WriteOutgoingMailboxEntry writes an Outgoing Mailbox entry
+  for Host Adapter with Action Code and CCB.
+*/
+
+static boolean BusLogic_WriteOutgoingMailboxEntry(BusLogic_HostAdapter_T
+						    *HostAdapter,
+						  BusLogic_ActionCode_T
+						    ActionCode,
+						  BusLogic_CCB_T *CCB)
+{
+  BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+  boolean Result = false;
+  BusLogic_LockHostAdapter(HostAdapter);
+  NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+  if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
+    {
+      NextOutgoingMailbox->ActionCode = ActionCode;
+      NextOutgoingMailbox->CCB = CCB;
+      CCB->Status = BusLogic_CCB_Active;
+      BusLogic_StartMailboxScan(HostAdapter);
+      if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+	NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+      HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+      Result = true;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return Result;
+}
+
+
+/*
+  BusLogic_QueueCommand creates a CCB for Command and places it into an
+  Outgoing Mailbox for execution by the associated Host Adapter.
+*/
+
+int BusLogic_QueueCommand(SCSI_Command_T *Command,
+			  void (*CompletionRoutine)(SCSI_Command_T *))
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char *CDB = Command->cmnd;
+  unsigned char CDB_Length = Command->cmd_len;
+  unsigned char TargetID = Command->target;
+  unsigned char LogicalUnit = Command->lun;
+  void *BufferPointer = Command->request_buffer;
+  int BufferLength = Command->request_bufflen;
+  int SegmentCount = Command->use_sg;
+  BusLogic_CCB_T *CCB;
+  long EnableTQ;
+  /*
+    SCSI REQUEST_SENSE commands will be executed automatically by the Host
+    Adapter for any errors, so they should not be executed explicitly unless
+    the Sense Data is zero indicating that no error occurred.
+  */
+  if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0)
+    {
+      Command->result = DID_OK << 16;
+      CompletionRoutine(Command);
+      return 0;
+    }
+  /*
+    Allocate a CCB from the Host Adapter's free list, aborting the command
+    with an error if there are none available and memory allocation fails.
+  */
+  CCB = BusLogic_AllocateCCB(HostAdapter);
+  if (CCB == NULL)
+    {
+      Command->result = DID_ERROR << 16;
+      CompletionRoutine(Command);
+      return 0;
+    }
+  /*
+    Initialize the fields in the BusLogic Command Control Block (CCB).
+  */
+  if (SegmentCount == 0)
+    {
+      CCB->Opcode = BusLogic_InitiatorCCB;
+      CCB->DataLength = BufferLength;
+      CCB->DataPointer = BufferPointer;
+    }
+  else
+    {
+      SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer;
+      int Segment;
+      CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
+      CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+      CCB->DataPointer = CCB->ScatterGatherList;
+      for (Segment = 0; Segment < SegmentCount; Segment++)
+	{
+	  CCB->ScatterGatherList[Segment].SegmentByteCount =
+	    ScatterList[Segment].length;
+	  CCB->ScatterGatherList[Segment].SegmentDataPointer =
+	    ScatterList[Segment].address;
+	}
+    }
+  switch (CDB[0])
+    {
+    case READ_6:
+    case READ_10:
+      CCB->DataDirection = BusLogic_DataInLengthChecked;
+      HostAdapter->ReadWriteOperationCount[TargetID]++;
+      break;
+    case WRITE_6:
+    case WRITE_10:
+      CCB->DataDirection = BusLogic_DataOutLengthChecked;
+      HostAdapter->ReadWriteOperationCount[TargetID]++;
+      break;
+    default:
+      CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+      break;
+    }
+  CCB->CDB_Length = CDB_Length;
+  CCB->SenseDataLength = sizeof(Command->sense_buffer);
+  CCB->TargetID = TargetID;
+  CCB->LogicalUnit = LogicalUnit;
+  /*
+    For Wide SCSI Host Adapters, Wide Mode CCBs are used to support more than
+    8 Logical Units per Target, and this requires setting the overloaded
+    TagEnable field to Logical Unit bit 5.
+  */
+  if (HostAdapter->HostWideSCSI)
+    {
+      CCB->TagEnable = LogicalUnit >> 5;
+      CCB->WideModeTagEnable = false;
+    }
+  else CCB->TagEnable = false;
+  /*
+    BusLogic recommends that after a Reset the first couple of commands that
+    are sent to a Target be sent in a non Tagged Queue fashion so that the Host
+    Adapter and Target can establish Synchronous Transfer before Queue Tag
+    messages can interfere with the Synchronous Negotiation message.
+  */
+  if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
+      Command->device->tagged_supported &&
+      (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 5) >= 0)
+    {
+      if (EnableTQ == 0)
+	printk("scsi%d: Tagged Queuing now active for Target %d\n",
+	       HostAdapter->HostNumber, TargetID);
+      if (HostAdapter->HostWideSCSI)
+	{
+	  CCB->WideModeTagEnable = true;
+	  CCB->WideModeQueueTag = BusLogic_SimpleQueueTag;
+	}
+      else
+	{
+	  CCB->TagEnable = true;
+	  CCB->QueueTag = BusLogic_SimpleQueueTag;
+	}
+    }
+  memcpy(CCB->CDB, CDB, CDB_Length);
+  CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer;
+  CCB->Command = Command;
+  Command->scsi_done = CompletionRoutine;
+  /*
+    Place the CCB in an Outgoing Mailbox, aborting the command with an
+    error if there are none available.
+  */
+  if (!(BusLogic_WriteOutgoingMailboxEntry(
+	  HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+    {
+      printk("scsi%d: cannot write Outgoing Mailbox Entry\n",
+	     HostAdapter->HostNumber);
+      BusLogic_DeallocateCCB(CCB);
+      Command->result = DID_ERROR << 16;
+      CompletionRoutine(Command);
+    }
+  return 0;
+}
+
+
+/*
+  BusLogic_AbortCommand aborts Command if possible.
+*/
+
+int BusLogic_AbortCommand(SCSI_Command_T *Command)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char InterruptRegister;
+  BusLogic_CCB_T *CCB;
+  int Result;
+  /*
+    If the Host Adapter has posted an interrupt but the Interrupt Handler
+    has not been called for some reason (i.e. the interrupt was lost), try
+    calling the Interrupt Handler directly to process the commands that
+    have been completed.
+  */
+  InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+  if (InterruptRegister & BusLogic_InterruptValid)
+    {
+      unsigned long ProcessorFlags;
+      printk("scsi%d: Recovering Lost Interrupt for IRQ Channel %d\n",
+	     HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+      save_flags(ProcessorFlags);
+      cli();
+      BusLogic_InterruptHandler(HostAdapter->IRQ_Channel, NULL);
+      restore_flags(ProcessorFlags);
+      return SCSI_ABORT_SNOOZE;
+    }
+  /*
+    Find the CCB to be aborted and determine how to proceed.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  Result = SCSI_ABORT_NOT_RUNNING;
+  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+    if (CCB->Command == Command)
+      {
+	if (CCB->Status == BusLogic_CCB_Active)
+	  if ((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable &&
+	       CCB->WideModeQueueTag != BusLogic_SimpleQueueTag) ||
+	      (!HostAdapter->HostWideSCSI && CCB->TagEnable &&
+	       CCB->QueueTag != BusLogic_SimpleQueueTag))
+	    {
+	      /*
+		CCBs using Tagged Queuing with other than Simple Queue Tag
+		should not be aborted.
+	      */
+	      Result = SCSI_ABORT_BUSY;
+	    }
+	  else
+	    {
+	      /*
+		Attempt to abort the CCB.
+	      */
+	      if (BusLogic_WriteOutgoingMailboxEntry(
+		    HostAdapter, BusLogic_MailboxAbortCommand, CCB))
+		{
+		  printk("scsi%d: Aborting CCB #%d\n",
+			 HostAdapter->HostNumber, CCB->SerialNumber);
+		  Result = SCSI_ABORT_PENDING;
+		}
+	      else Result = SCSI_ABORT_BUSY;
+	    }
+	break;
+      }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return Result;
+}
+
+
+/*
+  BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+  currently executing SCSI commands as having been reset, as well as
+  the specified Command if non-NULL.
+*/
+
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
+				     SCSI_Command_T *Command)
+{
+  BusLogic_CCB_T *CCB;
+  if (Command == NULL)
+    printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n",
+	   HostAdapter->HostNumber, HostAdapter->BoardName);
+  else printk("scsi%d: Resetting %s due to Target %d\n",
+	      HostAdapter->HostNumber, HostAdapter->BoardName, Command->target);
+  /*
+    Attempt to Reset and Reinitialize the Host Adapter.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  if (!(BusLogic_HardResetHostAdapter(HostAdapter) &&
+	BusLogic_InitializeHostAdapter(HostAdapter)))
+    {
+      printk("scsi%d: Resetting %s Failed\n",
+	      HostAdapter->HostNumber, HostAdapter->BoardName);
+      BusLogic_UnlockHostAdapter(HostAdapter);
+      return SCSI_RESET_ERROR;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  /*
+    Wait a few seconds between the Host Adapter Hard Reset which initiates
+    a SCSI Bus Reset and issuing any SCSI commands.  Some SCSI devices get
+    confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+  */
+  BusLogic_Delay(HostAdapter->BusSettleTime);
+  /*
+    Mark all currently executing CCBs as having been reset.
+  */
+  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+    if (CCB->Status == BusLogic_CCB_Active)
+      {
+	SCSI_Command_T *ActiveCommand = CCB->Command;
+	if (ActiveCommand == Command) Command = NULL;
+	BusLogic_DeallocateCCB(CCB);
+	if (ActiveCommand != NULL)
+	  {
+	    ActiveCommand->result = DID_RESET << 16;
+	    ActiveCommand->scsi_done(ActiveCommand);
+	  }
+      }
+  if (Command != NULL)
+    {
+      Command->result = DID_RESET << 16;
+      Command->scsi_done(Command);
+    }
+  return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+}
+
+
+/*
+  BusLogic_BusDeviceReset sends a Bus Device Reset to the Target
+  associated with Command.
+*/
+
+static int BusLogic_BusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
+				   SCSI_Command_T *Command)
+{
+  BusLogic_CCB_T *CCB = BusLogic_AllocateCCB(HostAdapter), *XCCB;
+  unsigned char TargetID = Command->target;
+  /*
+    If sending a Bus Device Reset is impossible, attempt a full Host
+    Adapter Hard Reset and SCSI Bus Reset.
+  */
+  if (CCB == NULL)
+    return BusLogic_ResetHostAdapter(HostAdapter, Command);
+  printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n",
+	 HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
+  CCB->Opcode = BusLogic_SCSIBusDeviceReset;
+  CCB->TargetID = TargetID;
+  CCB->Command = Command;
+  /*
+    If there is a currently executing CCB in the Host Adapter for this Command,
+    then an Incoming Mailbox entry will be made with a completion code of
+    BusLogic_HostAdapterAssertedBusDeviceReset.  Otherwise, the CCB Command
+    field will be left pointing to the Command so that the interrupt for the
+    completion of the Bus Device Reset can call the Completion Routine for the
+    Command.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+    if (XCCB->Status == BusLogic_CCB_Active && XCCB->Command == Command)
+      {
+	CCB->Command = NULL;
+	break;
+      }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  /*
+    Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
+    If sending a Bus Device Reset is impossible, attempt a full Host
+    Adapter Hard Reset and SCSI Bus Reset.
+  */
+  if (!(BusLogic_WriteOutgoingMailboxEntry(
+	  HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+    {
+      printk("scsi%d: cannot write Outgoing Mailbox Entry for "
+	     "Bus Device Reset\n", HostAdapter->HostNumber);
+      BusLogic_DeallocateCCB(CCB);
+      return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    }
+  HostAdapter->ReadWriteOperationCount[TargetID] = 0;
+  return SCSI_RESET_PENDING;
+}
+
+
+/*
+  BusLogic_ResetCommand takes appropriate action to reset Command.
+*/
+
+int BusLogic_ResetCommand(SCSI_Command_T *Command)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char TargetID = Command->target;
+  unsigned char ErrorRecoveryOption =
+    HostAdapter->ErrorRecoveryOption[TargetID];
+  if (ErrorRecoveryOption == BusLogic_ErrorRecoveryDefault)
+    if (Command->host->suggest_bus_reset)
+      ErrorRecoveryOption = BusLogic_ErrorRecoveryHardReset;
+    else ErrorRecoveryOption = BusLogic_ErrorRecoveryBusDeviceReset;
+  switch (ErrorRecoveryOption)
+    {
+    case BusLogic_ErrorRecoveryHardReset:
+      return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    case BusLogic_ErrorRecoveryBusDeviceReset:
+      if (HostAdapter->CommandSuccessfulFlag[TargetID])
+	{
+	  HostAdapter->CommandSuccessfulFlag[TargetID] = false;
+	  return BusLogic_BusDeviceReset(HostAdapter, Command);
+	}
+      else return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    }
+  printk("scsi%d: Error Recovery Suppressed\n", HostAdapter->HostNumber);
+  return SCSI_RESET_PUNT;
+}
+
+
+/*
+  BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+  Parameters for Disk.  The default disk geometry is 64 heads, 32 sectors, and
+  the appropriate number of cylinders so as not to exceed drive capacity.  In
+  order for disks equal to or larger than 1 GB to be addressable by the BIOS
+  without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
+  may be enabled in AutoSCSI on "C" Series boards or by a dip switch setting
+  on older boards.  With Extended Translation enabled, drives between 1 GB
+  inclusive and 2 GB exclusive are given a disk geometry of 128 heads and 32
+  sectors, and drives between 2 GB inclusive and 8 GB exclusive are given a
+  disk geometry of 255 heads and 63 sectors.  On "C" Series boards the firmware
+  can be queried for the precise translation in effect for each drive
+  individually, but there is really no need to do so since we know the total
+  capacity of the drive and whether Extended Translation is enabled, hence we
+  can deduce the BIOS disk geometry that must be in effect.
+*/
+
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
+				int *Parameters)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Disk->device->host->hostdata;
+  BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters;
+  if (HostAdapter->ExtendedTranslation &&
+      Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */)
+    if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */)
+      {
+	DiskParameters->Heads = 255;
+	DiskParameters->Sectors = 63;
+      }
+    else
+      {
+	DiskParameters->Heads = 128;
+	DiskParameters->Sectors = 32;
+      }
+  else
+    {
+      DiskParameters->Heads = 64;
+      DiskParameters->Sectors = 32;
+    }
+  DiskParameters->Cylinders =
+    Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+  return 0;
+}
+
+
+/*
+  BusLogic_Setup handles processing of Kernel Command Line Arguments.
+
+  For the BusLogic driver, a kernel command line entry comprises the driver
+  identifier "BusLogic=" optionally followed by a comma-separated sequence of
+  integers and then optionally followed by a comma-separated sequence of
+  strings.  Each command line entry applies to one BusLogic Host Adapter.
+  Multiple command line entries may be used in systems which contain multiple
+  BusLogic Host Adapters.
+
+  The first integer specified is the I/O Address at which the Host Adapter is
+  located.  If unspecified, it defaults to 0 which means to apply this entry to
+  the first BusLogic Host Adapter found during the default probe sequence.  If
+  any I/O Address parameters are provided on the command line, then the default
+  probe sequence is omitted.
+
+  The second integer specified is the number of Concurrent Commands per Logical
+  Unit to allow for Target Devices on the Host Adapter.  If unspecified, it
+  defaults to 0 which means to use the value of BusLogic_Concurrency for
+  non-ISA Host Adapters, or BusLogic_Concurrency_ISA for ISA Host Adapters.
+
+  The third integer specified is the Bus Settle Time in seconds.  This is
+  the amount of time to wait between a Host Adapter Hard Reset which initiates
+  a SCSI Bus Reset and issuing any SCSI commands.  If unspecified, it defaults
+  to 0 which means to use the value of BusLogic_DefaultBusSettleTime.
+
+  The fourth integer specified is the Tracing Options.  If unspecified, it
+  defaults to 0 which means that no special tracing information is to be
+  printed.  Note that Tracing Options are applied across all Host Adapters.
+
+  The string options are used to provide control over Tagged Queuing and Error
+  Recovery. If both Tagged Queuing and Error Recovery strings are provided, the
+  Tagged Queuing specification string must come first.
+
+  The Tagged Queuing specification begins with "TQ:" and allows for explicitly
+  specifying whether Tagged Queuing is permitted on Target Devices that support
+  it.  The following specification options are available:
+
+  TQ:Default		Tagged Queuing will be permitted based on the firmware
+			version of the BusLogic Host Adapter and based on
+			whether the Concurrency value allows queuing multiple
+			commands.
+
+  TQ:Enable		Tagged Queuing will be enabled for all Target Devices
+			on this Host Adapter overriding any limitation that
+			would otherwise be imposed based on the Host Adapter
+			firmware version.
+
+  TQ:Disable		Tagged Queuing will be disabled for all Target Devices
+			on this Host Adapter.
+
+  TQ:<Per-Target-Spec>	Tagged Queuing will be controlled individually for each
+			Target Device.  <Per-Target-Spec> is a sequence of "Y",
+			"N", and "X" characters.  "Y" enabled Tagged Queuing,
+			"N" disables Tagged Queuing, and "X" accepts the
+			default based on the firmware version.  The first
+			character refers to Target 0, the second to Target 1,
+			and so on; if the sequence of "Y", "N", and "X"
+			characters does not cover all the Target Devices,
+			unspecified characters are assumed to be "X".
+
+  Note that explicitly requesting Tagged Queuing may lead to problems; this
+  facility is provided primarily to allow disabling Tagged Queuing on Target
+  Devices that do not implement it correctly.
+
+  The Error Recovery specification begins with "ER:" and allows for explicitly
+  specifying the Error Recovery action to be performed when ResetCommand is
+  called due to a SCSI Command failing to complete successfully.  The following
+  specification options are available:
+
+  ER:Default		Error Recovery will select between the Hard Reset and
+			Bus Device Reset options based on the recommendation
+			of the SCSI Subsystem.
+
+  ER:HardReset		Error Recovery will initiate a Host Adapter Hard Reset
+			which also causes a SCSI Bus Reset.
+
+  ER:BusDeviceReset	Error Recovery will send a Bus Device Reset message to
+			the individual Target Device causing the error.  If
+			Error Recovery is again initiated for this Target
+			Device and no SCSI Command to this Target Device has
+			completed successfully since the Bus Device Reset
+			message was sent, then a Hard Reset will be attempted.
+
+  ER:None		Error Recovery will be suppressed.  This option should
+			only be selected if a SCSI Bus Reset or Bus Device
+			Reset will cause the Target Device to fail completely
+			and unrecoverably.
+
+  ER:<Per-Target-Spec>	Error Recovery will be controlled individually for each
+			Target Device.  <Per-Target-Spec> is a sequence of "D",
+			"H", "B", and "N" characters.  "D" selects Default, "H"
+			selects Hard Reset, "B" selects Bus Device Reset, and
+			"N" selects None.  The first character refers to Target
+			0, the second to Target 1, and so on; if the sequence
+			of "D", "H", "B", and "N" characters does not cover all
+			the Target Devices, unspecified characters are assumed
+			to be "D".
+*/
+
+void BusLogic_Setup(char *Strings, int *Integers)
+{
+  BusLogic_CommandLineEntry_T *CommandLineEntry =
+    &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
+  static int ProbeListIndex = 0;
+  int IntegerCount = Integers[0], TargetID, i;
+  CommandLineEntry->IO_Address = 0;
+  CommandLineEntry->Concurrency = 0;
+  CommandLineEntry->BusSettleTime = 0;
+  CommandLineEntry->TaggedQueuingPermitted = 0;
+  CommandLineEntry->TaggedQueuingPermittedMask = 0;
+  memset(CommandLineEntry->ErrorRecoveryOption,
+	 BusLogic_ErrorRecoveryDefault,
+	 sizeof(CommandLineEntry->ErrorRecoveryOption));
+  if (IntegerCount > 4)
+    printk("BusLogic: Unexpected Command Line Integers ignored\n");
+  if (IntegerCount >= 1)
+    {
+      unsigned short IO_Address = Integers[1];
+      if (IO_Address > 0)
+	{
+	  for (i = 0; ; i++)
+	    if (BusLogic_IO_StandardAddresses[i] == 0)
+	      {
+		printk("BusLogic: Invalid Command Line Entry "
+		       "(illegal I/O Address 0x%X)\n", IO_Address);
+		return;
+	      }
+	    else if (i < ProbeListIndex &&
+		     IO_Address == BusLogic_IO_AddressProbeList[i])
+	      {
+		printk("BusLogic: Invalid Command Line Entry "
+		       "(duplicate I/O Address 0x%X)\n", IO_Address);
+		return;
+	      }
+	    else if (IO_Address == BusLogic_IO_StandardAddresses[i]) break;
+	  BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address;
+	  BusLogic_IO_AddressProbeList[ProbeListIndex] = 0;
+	}
+      CommandLineEntry->IO_Address = IO_Address;
+    }
+  if (IntegerCount >= 2)
+    {
+      unsigned short Concurrency = Integers[2];
+      if (Concurrency > BusLogic_MailboxCount)
+	{
+	  printk("BusLogic: Invalid Command Line Entry "
+		 "(illegal Concurrency %d)\n", Concurrency);
+	  return;
+	}
+      CommandLineEntry->Concurrency = Concurrency;
+    }
+  if (IntegerCount >= 3)
+    CommandLineEntry->BusSettleTime = Integers[3];
+  if (IntegerCount >= 4)
+    BusLogic_TracingOptions |= Integers[4];
+  if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 ||
+	BusLogic_CommandLineEntryCount == ProbeListIndex))
+    {
+      printk("BusLogic: Invalid Command Line Entry "
+	     "(all or no I/O Addresses must be specified)\n");
+      return;
+    }
+  if (Strings == NULL) return;
+  if (strncmp(Strings, "TQ:", 3) == 0)
+    {
+      Strings += 3;
+      if (strncmp(Strings, "Default", 7) == 0)
+	Strings += 7;
+      else if (strncmp(Strings, "Enable", 6) == 0)
+	{
+	  Strings += 6;
+	  CommandLineEntry->TaggedQueuingPermitted = 0xFFFF;
+	  CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+	}
+      else if (strncmp(Strings, "Disable", 7) == 0)
+	{
+	  Strings += 7;
+	  CommandLineEntry->TaggedQueuingPermitted = 0x0000;
+	  CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+	}
+      else
+	for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+	  switch (*Strings++)
+	    {
+	    case 'Y':
+	      CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID;
+	      CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+	      break;
+	    case 'N':
+	      CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+	      break;
+	    case 'X':
+	      break;
+	    default:
+	      Strings--;
+	      TargetID = BusLogic_MaxTargetIDs;
+	      break;
+	    }
+    }
+  if (*Strings == ',') Strings++;
+  if (strncmp(Strings, "ER:", 3) == 0)
+    {
+      Strings += 3;
+      if (strncmp(Strings, "Default", 7) == 0)
+	Strings += 7;
+      else if (strncmp(Strings, "HardReset", 9) == 0)
+	{
+	  Strings += 9;
+	  memset(CommandLineEntry->ErrorRecoveryOption,
+		 BusLogic_ErrorRecoveryHardReset,
+		 sizeof(CommandLineEntry->ErrorRecoveryOption));
+	}
+      else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
+	{
+	  Strings += 14;
+	  memset(CommandLineEntry->ErrorRecoveryOption,
+		 BusLogic_ErrorRecoveryBusDeviceReset,
+		 sizeof(CommandLineEntry->ErrorRecoveryOption));
+	}
+      else if (strncmp(Strings, "None", 4) == 0)
+	{
+	  Strings += 4;
+	  memset(CommandLineEntry->ErrorRecoveryOption,
+		 BusLogic_ErrorRecoveryNone,
+		 sizeof(CommandLineEntry->ErrorRecoveryOption));
+	}
+      else
+	for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+	  switch (*Strings++)
+	    {
+	    case 'D':
+	      CommandLineEntry->ErrorRecoveryOption[TargetID] =
+		BusLogic_ErrorRecoveryDefault;
+	      break;
+	    case 'H':
+	      CommandLineEntry->ErrorRecoveryOption[TargetID] =
+		BusLogic_ErrorRecoveryHardReset;
+	      break;
+	    case 'B':
+	      CommandLineEntry->ErrorRecoveryOption[TargetID] =
+		BusLogic_ErrorRecoveryBusDeviceReset;
+	      break;
+	    case 'N':
+	      CommandLineEntry->ErrorRecoveryOption[TargetID] =
+		BusLogic_ErrorRecoveryNone;
+	      break;
+	    default:
+	      Strings--;
+	      TargetID = BusLogic_MaxTargetIDs;
+	      break;
+	    }
+    }
+  if (*Strings != '\0')
+    printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings);
+}
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v1.3.41/linux/drivers/scsi/BusLogic.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/BusLogic.h	Sat Nov 11 23:40:42 1995
@@ -0,0 +1,967 @@
+/*
+
+  Linux Driver for BusLogic SCSI Host Adapters
+
+  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  See BusLogic.c for licensing information.
+
+*/
+
+
+/*
+  Define types for some of the structures that interface with the rest
+  of the Linux Kernel and SCSI Subsystem.
+*/
+
+typedef struct pt_regs Registers_T;
+typedef Scsi_Host_Template SCSI_Host_Template_T;
+typedef struct Scsi_Host SCSI_Host_T;
+typedef struct scsi_disk SCSI_Disk_T;
+typedef struct scsi_cmnd SCSI_Command_T;
+typedef struct scatterlist SCSI_ScatterList_T;
+typedef kdev_t KernelDevice_T;
+
+
+/*
+  Define prototypes for the BusLogic Driver Interface Functions.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *);
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
+int BusLogic_QueueCommand(SCSI_Command_T *,
+			  void (*CompletionRoutine)(SCSI_Command_T *));
+int BusLogic_AbortCommand(SCSI_Command_T *);
+int BusLogic_ResetCommand(SCSI_Command_T *);
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+
+
+/*
+  Define the BusLogic SCSI Host Template structure.
+*/
+
+#define BUSLOGIC							 \
+  { NULL,				/* Next			     */  \
+    NULL,				/* Usage Count Pointer	     */  \
+    NULL,				/* /proc Directory Entry     */	 \
+    NULL,				/* /proc Info Function	     */	 \
+    "BusLogic",				/* Driver Name		     */  \
+    BusLogic_DetectHostAdapter,		/* Detect Host Adapter	     */  \
+    BusLogic_ReleaseHostAdapter,	/* Release Host Adapter	     */  \
+    BusLogic_DriverInfo,		/* Driver Info Function	     */  \
+    NULL,				/* Command Function	     */  \
+    BusLogic_QueueCommand,		/* Queue Command Function    */  \
+    BusLogic_AbortCommand,		/* Abort Command Function    */  \
+    BusLogic_ResetCommand,		/* Reset Command Function    */  \
+    NULL,				/* Slave Attach Function     */  \
+    BusLogic_BIOSDiskParameters,	/* Disk BIOS Parameters	     */  \
+    0,					/* Can Queue		     */  \
+    0,					/* This ID		     */  \
+    0,					/* Scatter/Gather Table Size */  \
+    0,					/* SCSI Commands per LUN     */  \
+    0,					/* Present		     */  \
+    1,					/* Default Unchecked ISA DMA */  \
+    ENABLE_CLUSTERING }			/* Enable Clustering	     */
+
+
+/*
+  BusLogic_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef BusLogic_DriverVersion
+
+
+/*
+  Define the maximum number of BusLogic Host Adapters that are supported.
+*/
+
+#define BusLogic_MaxHostAdapters		10
+
+
+/*
+  Define the maximum number of I/O Addresses that may be probed.
+*/
+
+#define BusLogic_IO_MaxProbeAddresses		16
+
+
+/*
+  Define the maximum number of Target IDs supported by this driver.
+*/
+
+#define BusLogic_MaxTargetIDs			16
+
+
+/*
+  Define the number of Incoming and Outgoing Mailboxes used by this driver.
+  Since BusLogic Host Adapters have room to buffer 32 commands internally,
+  there is significant virtue in setting BusLogic_MailboxCount to 32 or above.
+  The maximum possible value is 255, since the MailboxCount parameter to the
+  Initialize Extended Mailbox command is limited to a single byte.
+*/
+
+#define BusLogic_MailboxCount			32
+
+
+/*
+  Define the number of Command Control Blocks (CCBs) to create during
+  initialization for each Host Adapter.  Additional CCBs will be allocated
+  if necessary as commands are queued.
+*/
+
+#define BusLogic_InitialCCBs			32
+
+
+/*
+  Define the maximum number of Scatter/Gather Segments used by this driver.
+  For maximum performance, it is important that this limit be at least as
+  large as the maximum single request generated by the routine make_request.
+*/
+
+#define BusLogic_ScatterGatherLimit		128
+
+
+/*
+  Define the default number of Concurrent Commands per Logical Unit to allow
+  for Target Devices on non-ISA and ISA Host Adapters.
+*/
+
+#define BusLogic_Concurrency			7
+#define BusLogic_Concurrency_ISA		1
+
+
+/*
+  Define the default amount of time in seconds to wait between a Host Adapter
+  Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands.
+  Some SCSI devices get confused if they receive SCSI commands too soon after
+  a SCSI Bus Reset.
+*/
+
+#define BusLogic_DefaultBusSettleTime		2
+
+
+/*
+  Define the possible Tracing Options.
+*/
+
+#define BusLogic_TraceProbe			1
+#define BusLogic_TraceHardReset			2
+#define BusLogic_TraceConfiguration		4
+#define BusLogic_TraceErrors			8
+
+
+/*
+  Define the possible Error Recovery Options.
+*/
+
+#define BusLogic_ErrorRecoveryDefault		0
+#define BusLogic_ErrorRecoveryHardReset		1
+#define BusLogic_ErrorRecoveryBusDeviceReset	2
+#define BusLogic_ErrorRecoveryNone		3
+
+static char
+  *BusLogic_ErrorRecoveryOptions[] =
+    { "Default", "Hard Reset", "Bus Device Reset", "None" },
+  *BusLogic_ErrorRecoveryOptions2[] =
+    { "D", "H", "B", "N" };
+
+
+/*
+  Define a boolean data type.
+*/
+
+#define false 0
+#define true  1
+typedef unsigned char boolean;
+
+
+/*
+  Define the BusLogic SCSI Host Adapter I/O Register Offsets.
+*/
+
+#define BusLogic_IO_PortCount			4	/* I/O Registers */
+#define BusLogic_ControlRegister		0	/* WO register */
+#define BusLogic_StatusRegister			0	/* RO register */
+#define BusLogic_CommandParameterRegister	1	/* WO register */
+#define BusLogic_DataInRegister			1	/* RO register */
+#define BusLogic_InterruptRegister		2	/* RO register */
+#define BusLogic_GeometryRegister		3	/* RO, undocumented */
+
+
+/*
+  Define the bits in the write-only Control Register.
+*/
+
+#define BusLogic_ReservedCR			0x0F
+#define BusLogic_SCSIBusReset			0x10
+#define BusLogic_InterruptReset			0x20
+#define BusLogic_SoftReset			0x40
+#define BusLogic_HardReset			0x80
+
+
+/*
+  Define the bits in the read-only Status Register.
+*/
+
+#define BusLogic_CommandInvalid			0x01
+#define BusLogic_ReservedSR			0x02
+#define BusLogic_DataInRegisterReady		0x04
+#define BusLogic_CommandParameterRegisterBusy	0x08
+#define BusLogic_HostAdapterReady		0x10
+#define BusLogic_InitializationRequired		0x20
+#define BusLogic_DiagnosticFailure		0x40
+#define BusLogic_DiagnosticActive		0x80
+
+
+/*
+  Define the bits in the read-only Interrupt Register.
+*/
+
+#define BusLogic_IncomingMailboxLoaded		0x01
+#define BusLogic_OutgoingMailboxAvailable	0x02
+#define BusLogic_CommandComplete		0x04
+#define BusLogic_SCSIResetState			0x08
+#define BusLogic_ReservedIR			0x70
+#define BusLogic_InterruptValid			0x80
+
+
+/*
+  Define the bits in the undocumented read-only Geometry Register.
+*/
+
+#define BusLogic_Drive0Geometry			0x03
+#define BusLogic_Drive1Geometry			0x0C
+#define BusLogic_ReservedGR			0x70
+#define BusLogic_ExtendedTranslationEnabled	0x80
+
+
+/*
+  Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
+*/
+
+typedef enum
+{
+  BusLogic_TestCommandCompleteInterrupt =	0x00,	/* documented */
+  BusLogic_InitializeMailbox =			0x01,	/* documented */
+  BusLogic_StartMailboxCommand =		0x02,	/* documented */
+  BusLogic_StartBIOSCommand =			0x03,	/* documented */
+  BusLogic_InquireBoardID =			0x04,	/* documented */
+  BusLogic_EnableOutgoingMailboxAvailableIRQ =	0x05,	/* documented */
+  BusLogic_SetSCSISelectionTimeout =		0x06,	/* documented */
+  BusLogic_SetPreemptTimeOnBus =		0x07,	/* documented */
+  BusLogic_SetTimeOffBus =			0x08,	/* ISA Bus only */
+  BusLogic_SetBusTransferRate =			0x09,	/* ISA Bus only */
+  BusLogic_InquireInstalledDevicesID0to7 =	0x0A,	/* documented */
+  BusLogic_InquireConfiguration =		0x0B,	/* documented */
+  BusLogic_SetTargetMode =			0x0C,	/* now undocumented */
+  BusLogic_InquireSetupInformation =		0x0D,	/* documented */
+  BusLogic_WriteAdapterLocalRAM =		0x1A,	/* documented */
+  BusLogic_ReadAdapterLocalRAM =		0x1B,	/* documented */
+  BusLogic_WriteBusMasterChipFIFO =		0x1C,	/* documented */
+  BusLogic_ReadBusMasterChipFIFO =		0x1D,	/* documented */
+  BusLogic_EchoCommandData =			0x1F,	/* documented */
+  BusLogic_HostAdapterDiagnostic =		0x20,	/* documented */
+  BusLogic_SetAdapterOptions =			0x21,	/* documented */
+  BusLogic_InquireInstalledDevicesID8to15 =	0x23,	/* Wide only */
+  BusLogic_InitializeExtendedMailbox =		0x81,	/* documented */
+  BusLogic_InquireFirmwareVersion3rdDigit =	0x84,	/* undocumented */
+  BusLogic_InquireFirmwareVersionLetter =	0x85,	/* undocumented */
+  BusLogic_InquireBoardModelAndRevision =	0x8B,	/* undocumented */
+  BusLogic_InquireSynchronousPeriod =		0x8C,	/* undocumented */
+  BusLogic_InquireExtendedSetupInformation =	0x8D,	/* documented */
+  BusLogic_EnableStrictRoundRobinMode =		0x8F,	/* documented */
+  BusLogic_ModifyIOAddress =			0x95,	/* PCI only */
+  BusLogic_EnableWideModeCCB =			0x96	/* Wide only */
+}
+BusLogic_OperationCode_T;
+
+
+/*
+  Define the Inquire Board ID reply structure.
+*/
+
+typedef struct BusLogic_BoardID
+{
+  unsigned char BoardType;
+  unsigned char CustomFeatures;
+  unsigned char FirmwareVersion1stDigit;
+  unsigned char FirmwareVersion2ndDigit;
+}
+BusLogic_BoardID_T;
+
+
+/*
+  Define the Inquire Installed Devices ID 0 to 7 and Inquire Installed
+  Devices ID 8 to 15 reply type.  For each Target ID, a byte is returned
+  where bit 0 set indicates that Logical Unit 0 exists, bit 1 set indicates
+  that Logical Unit 1 exists, and so on.
+*/
+
+typedef unsigned char BusLogic_InstalledDevices8_T[8];
+
+typedef unsigned char BusLogic_InstalledDevices_T[BusLogic_MaxTargetIDs];
+
+
+/*
+  Define the Inquire Configuration reply structure.
+*/
+
+typedef struct BusLogic_Configuration
+{
+  unsigned char :5;				/* Byte 0: DMA Channel */
+  boolean DMA_Channel5:1;
+  boolean DMA_Channel6:1;
+  boolean DMA_Channel7:1;
+  boolean IRQ_Channel9:1;			/* Byte 1: IRQ Channel */
+  boolean IRQ_Channel10:1;
+  boolean IRQ_Channel11:1;
+  boolean IRQ_Channel12:1;
+  unsigned char :1;
+  boolean IRQ_Channel14:1;
+  boolean IRQ_Channel15:1;
+  unsigned char :1;
+  unsigned char HostAdapterID:4;		/* Byte 2: Host Adapter ID */
+  unsigned char :4;
+}
+BusLogic_Configuration_T;
+
+
+/*
+  Define the Inquire Setup Information reply structure.
+*/
+
+typedef struct BusLogic_SynchronousValue
+{
+  unsigned char Offset:4;
+  unsigned char TransferPeriod:3;
+  boolean Synchronous:1;
+}
+BusLogic_SynchronousValue_T;
+
+typedef BusLogic_SynchronousValue_T
+  BusLogic_SynchronousValues8_T[8];
+
+typedef BusLogic_SynchronousValue_T
+  BusLogic_SynchronousValues_T[BusLogic_MaxTargetIDs];
+
+typedef struct BusLogic_SetupInformation
+{
+  boolean SynchronousInitiationEnabled:1;	/* Byte 0 */
+  boolean ParityCheckEnabled:1;
+  unsigned char :6;
+  unsigned char BusTransferRate;		/* Byte 1 */
+  unsigned char PreemptTimeOnBus;		/* Byte 2 */
+  unsigned char TimeOffBus;			/* Byte 3 */
+  unsigned char MailboxCount;			/* Byte 4 */
+  unsigned char MailboxAddress[3];		/* Bytes 5-7 */
+  BusLogic_SynchronousValues8_T SynchronousValuesID0to7; /* Bytes 8-15 */
+  unsigned char DisconnectPermittedID0to7;	/* Byte 16 */
+  unsigned char Signature;			/* Byte 17 */
+  unsigned char CharacterD;			/* Byte 18 */
+  unsigned char BusLetter;			/* Byte 19 */
+  unsigned char :8;				/* Byte 20 */
+  unsigned char :8;				/* Byte 21 */
+  BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */
+  unsigned char DisconnectPermittedID8to15;	/* Byte 30 */
+}
+BusLogic_SetupInformation_T;
+
+
+/*
+  Define the Initialize Extended Mailbox request structure.
+*/
+
+typedef struct BusLogic_ExtendedMailboxRequest
+{
+  unsigned char MailboxCount;
+  void *BaseMailboxAddress __attribute__ ((packed));
+}
+BusLogic_ExtendedMailboxRequest_T;
+
+
+/*
+  Define the Inquire Firmware Version 3rd Digit reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersion3rdDigit_T;
+
+
+/*
+  Define the Inquire Firmware Version Letter reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersionLetter_T;
+
+
+/*
+  Define the Inquire Board Model and Revision reply structure.
+*/
+
+typedef struct BusLogic_ModelAndRevision
+{
+  unsigned char Model[5];
+  unsigned char Revision;
+}
+BusLogic_ModelAndRevision_T;
+
+
+/*
+  Define the Inquire Synchronous Period reply type.  For each Target ID, a byte
+  is returned which represents the Synchronous Transfer Period in units of 10
+  nanoseconds.
+*/
+
+typedef unsigned char BusLogic_SynchronousPeriod_T[BusLogic_MaxTargetIDs];
+
+
+/*
+  Define the Inquire Extended Setup Information reply structure.
+*/
+
+typedef struct BusLogic_ExtendedSetupInformation
+{
+  unsigned char BusType;				/* Byte 0 */
+  unsigned char BIOS_Address;				/* Byte 1 */
+  unsigned short ScatterGatherLimit;			/* Bytes 2-3 */
+  unsigned char MailboxCount;				/* Byte 4 */
+  void *BaseMailboxAddress __attribute__ ((packed));	/* Bytes 5-8 */
+  struct { unsigned char :6;				/* Byte 9 */
+	   boolean LevelTriggeredInterrupts:1;
+	   unsigned char :1; } Misc;
+  unsigned char FirmwareRevision[3];			/* Bytes 10-12 */
+  boolean HostWideSCSI:1;				/* Byte 13 Bit 0 */
+  boolean HostDifferentialSCSI:1;			/* Byte 13 Bit 1 */
+  unsigned char :6;
+}
+BusLogic_ExtendedSetupInformation_T;
+
+
+/*
+  Define the Enable Strict Round Robin Mode request type.
+*/
+
+#define BusLogic_AggressiveRoundRobinMode	0x00
+#define BusLogic_StrictRoundRobinMode		0x01
+
+typedef unsigned char BusLogic_RoundRobinModeRequest_T;
+
+
+/*
+  Define the Modify I/O Address request type.  On PCI Host Adapters, the
+  Modify I/O Address command allows modification of the ISA compatible I/O
+  Address that the Host Adapter responds to; it does not affect the PCI
+  compliant I/O Address assigned at system initialization.
+*/
+
+#define BusLogic_ModifyIO_330			0x00
+#define BusLogic_ModifyIO_334			0x01
+#define BusLogic_ModifyIO_230			0x02
+#define BusLogic_ModifyIO_234			0x03
+#define BusLogic_ModifyIO_130			0x04
+#define BusLogic_ModifyIO_134			0x05
+#define BusLogic_ModifyIO_Disable		0x06
+#define BusLogic_ModifyIO_Disable2		0x07
+
+typedef unsigned char BusLogic_ModifyIOAddressRequest_T;
+
+
+/*
+  Define the Enable Wide Mode SCSI CCB request type.  Wide Mode CCBs are
+  necessary to support more than 8 Logical Units per Target.
+*/
+
+#define BusLogic_NormalModeCCB			0x00
+#define BusLogic_WideModeCCB			0x01
+
+typedef unsigned char BusLogic_WideModeCCBRequest_T;
+
+
+/*
+  Define the Requested Reply Length type used by the Inquire Setup Information,
+  Inquire Board Model and Revision, Inquire Synchronous Period, and Inquire
+  Extended Setup Information commands.
+*/
+
+typedef unsigned char BusLogic_RequestedReplyLength_T;
+
+
+/*
+  Define a Lock data structure.  Until a true symmetric multiprocessing kernel
+  is available, locking is implemented as saving the processor flags and
+  disabling interrupts, and unlocking restores the saved processor flags.
+*/
+
+typedef unsigned long BusLogic_Lock_T;
+
+
+/*
+  Define the Outgoing Mailbox Action Codes.
+*/
+
+typedef enum
+{
+  BusLogic_OutgoingMailboxFree =		0,
+  BusLogic_MailboxStartCommand =		1,
+  BusLogic_MailboxAbortCommand =		2
+}
+BusLogic_ActionCode_T;
+
+
+/*
+  Define the Incoming Mailbox Completion Codes.
+*/
+
+typedef enum
+{
+  BusLogic_IncomingMailboxFree =		0,
+  BusLogic_CommandCompletedWithoutError =	1,
+  BusLogic_CommandAbortedAtHostRequest =	2,
+  BusLogic_AbortedCommandNotFound =		3,
+  BusLogic_CommandCompletedWithError =		4
+}
+BusLogic_CompletionCode_T;
+
+
+/*
+  Define the Command Control Block (CCB) Opcodes.
+*/
+
+typedef enum
+{
+  BusLogic_InitiatorCCB =			0x00,
+  BusLogic_TargetCCB =				0x01,
+  BusLogic_InitiatorCCB_ScatterGather =		0x02,
+  BusLogic_InitiatorCCB_ResidualDataLength =	0x03,
+  BusLogic_InitiatorCCB_ScatterGatherResidual =	0x04,
+  BusLogic_SCSIBusDeviceReset =			0x81
+}
+BusLogic_CCB_Opcode_T;
+
+
+/*
+  Define the CCB Data Direction Codes.
+*/
+
+typedef enum
+{
+  BusLogic_UncheckedDataTransfer =		0x00,
+  BusLogic_DataInLengthChecked =		0x01,
+  BusLogic_DataOutLengthChecked =		0x02,
+  BusLogic_NoDataTransfer =			0x03
+}
+BusLogic_DataDirection_T;
+
+
+/*
+  Define the Host Adapter Status Codes.
+*/
+
+typedef enum
+{
+  BusLogic_CommandCompletedNormally =		0x00,
+  BusLogic_LinkedCommandCompleted =		0x0A,
+  BusLogic_LinkedCommandCompletedWithFlag =	0x0B,
+  BusLogic_SCSISelectionTimeout =		0x11,
+  BusLogic_DataOverUnderRun =			0x12,
+  BusLogic_UnexpectedBusFree =			0x13,
+  BusLogic_InvalidBusPhaseRequested =		0x14,
+  BusLogic_InvalidOutgoingMailboxActionCode =	0x15,
+  BusLogic_InvalidCommandOperationCode =	0x16,
+  BusLogic_LinkedCCBhasInvalidLUN =		0x17,
+  BusLogic_InvalidCommandParameter =		0x1A,
+  BusLogic_AutoRequestSenseFailed =		0x1B,
+  BusLogic_TaggedQueuingMessageRejected =	0x1C,
+  BusLogic_UnsupportedMessageReceived =		0x1D,
+  BusLogic_HostAdapterHardwareFailed =		0x20,
+  BusLogic_TargetFailedResponseToATN =		0x21,
+  BusLogic_HostAdapterAssertedRST =		0x22,
+  BusLogic_OtherDeviceAssertedRST =		0x23,
+  BusLogic_TargetDeviceReconnectedImproperly =	0x24,
+  BusLogic_HostAdapterAssertedBusDeviceReset =	0x25,
+  BusLogic_AbortQueueGenerated =		0x26,
+  BusLogic_HostAdapterSoftwareError =		0x27,
+  BusLogic_HostAdapterHardwareTimeoutError =	0x30,
+  BusLogic_SCSIParityErrorDetected =		0x34
+}
+BusLogic_HostAdapterStatus_T;
+
+
+/*
+  Define the SCSI Target Device Status Codes.
+*/
+
+typedef enum
+{
+  BusLogic_OperationGood =			0x00,
+  BusLogic_CheckCondition =			0x02,
+  BusLogic_DeviceBusy =				0x08
+}
+BusLogic_TargetDeviceStatus_T;
+
+
+/*
+  Define the Queue Tag Codes.
+*/
+
+typedef enum
+{
+  BusLogic_SimpleQueueTag =			0x00,
+  BusLogic_HeadOfQueueTag =			0x01,
+  BusLogic_OrderedQueueTag =			0x02,
+  BusLogic_ReservedQT =				0x03
+}
+BusLogic_QueueTag_T;
+
+
+/*
+  Define the SCSI Command Descriptor Block (CDB).
+*/
+
+#define BusLogic_CDB_MaxLength			12
+
+typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+
+
+/*
+  Define the SCSI Sense Data.
+*/
+
+#define BusLogic_SenseDataMaxLength		255
+
+typedef unsigned char SCSI_SenseData_T[BusLogic_SenseDataMaxLength];
+
+
+/*
+  Define the Scatter/Gather Segment structure required by the Host Adapter
+  Firmware Interface.
+*/
+
+typedef struct BusLogic_ScatterGatherSegment
+{
+  unsigned long SegmentByteCount;
+  void *SegmentDataPointer;
+}
+BusLogic_ScatterGatherSegment_T;
+
+
+/*
+  Define the 32 Bit Mode Command Control Block (CCB) structure.  The first 40
+  bytes are defined by the Host Adapter Firmware Interface.  The remaining
+  components are defined by the Linux BusLogic Driver.  Wide Mode CCBs differ
+  from standard 32 Bit Mode CCBs only in having the TagEnable and QueueTag
+  fields moved from byte 17 to byte 1, and the Logical Unit field in byte 17
+  expanded to 6 bits; unfortunately, using a union of structs containing
+  enumeration type bitfields to provide both definitions leads to packing
+  problems, so the following definition is used which requires setting
+  TagEnable to Logical Unit bit 5 in Wide Mode CCBs.
+*/
+
+typedef struct BusLogic_CCB
+{
+  /*
+    BusLogic Host Adapter Firmware Portion.
+  */
+  BusLogic_CCB_Opcode_T Opcode:8;			/* Byte 0 */
+  unsigned char :3;					/* Byte 1 Bits 0-2 */
+  BusLogic_DataDirection_T DataDirection:2;		/* Byte 1 Bits 3-4 */
+  boolean WideModeTagEnable:1;				/* Byte 1 Bit 5 */
+  BusLogic_QueueTag_T WideModeQueueTag:2;		/* Byte 1 Bits 6-7 */
+  unsigned char CDB_Length;				/* Byte 2 */
+  unsigned char SenseDataLength;			/* Byte 3 */
+  unsigned long DataLength;				/* Bytes 4-7 */
+  void *DataPointer;					/* Bytes 8-11 */
+  unsigned char :8;					/* Byte 12 */
+  unsigned char :8;					/* Byte 13 */
+  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 14 */
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;	/* Byte 15 */
+  unsigned char TargetID;				/* Byte 16 */
+  unsigned char LogicalUnit:5;				/* Byte 17 Bits 0-2 */
+  boolean TagEnable:1;					/* Byte 17 Bit 5 */
+  BusLogic_QueueTag_T QueueTag:2;			/* Byte 17 Bits 6-7 */
+  SCSI_CDB_T CDB;					/* Bytes 18-29 */
+  unsigned char :8;					/* Byte 30 */
+  unsigned char :8;					/* Byte 31 */
+  unsigned long :32;					/* Bytes 32-35 */
+  SCSI_SenseData_T *SenseDataPointer;			/* Bytes 36-39 */
+  /*
+    BusLogic Linux Driver Portion.
+  */
+  struct BusLogic_HostAdapter *HostAdapter;
+  SCSI_Command_T *Command;
+  enum { BusLogic_CCB_Free =	    0,
+	 BusLogic_CCB_Active =	    1,
+	 BusLogic_CCB_Completed =   2 } Status;
+  BusLogic_CompletionCode_T MailboxCompletionCode;
+  unsigned int SerialNumber;
+  struct BusLogic_CCB *Next;
+  struct BusLogic_CCB *NextAll;
+  BusLogic_ScatterGatherSegment_T
+    ScatterGatherList[BusLogic_ScatterGatherLimit];
+}
+BusLogic_CCB_T;
+
+
+/*
+  Define the 32 Bit Mode Outgoing Mailbox structure.
+*/
+
+typedef struct BusLogic_OutgoingMailbox
+{
+  BusLogic_CCB_T *CCB;
+  unsigned long :24;
+  BusLogic_ActionCode_T ActionCode:8;
+}
+BusLogic_OutgoingMailbox_T;
+
+
+/*
+  Define the 32 Bit Mode Incoming Mailbox structure.
+*/
+
+typedef struct BusLogic_IncomingMailbox
+{
+  BusLogic_CCB_T *CCB;
+  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;
+  unsigned char :8;
+  BusLogic_CompletionCode_T CompletionCode:8;
+}
+BusLogic_IncomingMailbox_T;
+
+
+/*
+  Define the possible Bus Types.
+*/
+
+typedef enum
+{
+  BusLogic_Unknown_Bus =			0,
+  BusLogic_ISA_Bus =				1,
+  BusLogic_MCA_Bus =				2,
+  BusLogic_EISA_Bus =				3,
+  BusLogic_VESA_Bus =				4,
+  BusLogic_PCI_Bus =				5
+}
+BusLogic_BusType_T;
+
+static char
+  *BusLogic_BusNames[] =
+    { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" };
+
+
+/*
+  Define the Linux BusLogic Driver Command Line Entry structure.
+*/
+
+typedef struct BusLogic_CommandLineEntry
+{
+  unsigned short IO_Address;
+  unsigned short Concurrency;
+  unsigned short BusSettleTime;
+  unsigned short TaggedQueuingPermitted;
+  unsigned short TaggedQueuingPermittedMask;
+  unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+}
+BusLogic_CommandLineEntry_T;
+
+
+/*
+  Define the Linux BusLogic Driver Host Adapter structure.
+*/
+
+typedef struct BusLogic_HostAdapter
+{
+  SCSI_Host_T *SCSI_Host;
+  unsigned char HostNumber;
+  unsigned char ModelName[6];
+  unsigned char FirmwareVersion[6];
+  unsigned char BoardName[15];
+  unsigned char InterruptLabel[63];
+  unsigned short IO_Address;
+  unsigned char IRQ_Channel;
+  unsigned char DMA_Channel;
+  unsigned char SCSI_ID;
+  BusLogic_BusType_T BusType:3;
+  boolean IRQ_ChannelAcquired:1;
+  boolean DMA_ChannelAcquired:1;
+  boolean SynchronousInitiation:1;
+  boolean ParityChecking:1;
+  boolean ExtendedTranslation:1;
+  boolean LevelTriggeredInterrupts:1;
+  boolean HostWideSCSI:1;
+  boolean HostDifferentialSCSI:1;
+  boolean HostAdapterResetPending:1;
+  volatile boolean HostAdapterCommandCompleted:1;
+  unsigned short HostAdapterScatterGatherLimit;
+  unsigned short DriverScatterGatherLimit;
+  unsigned short MaxTargetIDs;
+  unsigned short MaxLogicalUnits;
+  unsigned short Concurrency;
+  unsigned short BusSettleTime;
+  unsigned short DisconnectPermitted;
+  unsigned short TaggedQueuingPermitted;
+  unsigned long BIOS_Address;
+  BusLogic_InstalledDevices_T InstalledDevices;
+  BusLogic_SynchronousValues_T SynchronousValues;
+  BusLogic_SynchronousPeriod_T SynchronousPeriod;
+  BusLogic_Lock_T Lock;
+  struct BusLogic_HostAdapter *Next;
+  BusLogic_CommandLineEntry_T *CommandLineEntry;
+  BusLogic_CCB_T *All_CCBs;
+  BusLogic_CCB_T *Free_CCBs;
+  unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+  unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetIDs];
+  unsigned long ReadWriteOperationCount[BusLogic_MaxTargetIDs];
+  BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
+  BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
+  BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+  BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
+  BusLogic_IncomingMailbox_T *LastIncomingMailbox;
+  BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+  BusLogic_OutgoingMailbox_T OutgoingMailboxes[BusLogic_MailboxCount];
+  BusLogic_IncomingMailbox_T IncomingMailboxes[BusLogic_MailboxCount];
+}
+BusLogic_HostAdapter_T;
+
+
+/*
+  Define a symbolic structure for the BIOS Disk Parameters.
+*/
+
+typedef struct BIOS_DiskParameters
+{
+  int Heads;
+  int Sectors;
+  int Cylinders;
+}
+BIOS_DiskParameters_T;
+
+
+/*
+  BusLogic_LockHostAdapter acquires exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_LockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  save_flags(HostAdapter->Lock);
+  cli();
+}
+
+
+/*
+  BusLogic_UnlockHostAdapter releases exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  restore_flags(HostAdapter->Lock);
+}
+
+
+/*
+  BusLogic_LockHostAdapterID acquires exclusive access to Host Adapter,
+  but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_LockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+  BusLogic_UnlockHostAdapterID releases exclusive access to Host Adapter,
+  but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+  Define functions to provide an abstraction for reading and writing the
+  Host Adapter I/O Registers.
+*/
+
+static inline
+void BusLogic_WriteControlRegister(BusLogic_HostAdapter_T *HostAdapter,
+				   unsigned char Value)
+{
+  outb(Value, HostAdapter->IO_Address + BusLogic_ControlRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_StatusRegister);
+}
+
+static inline
+void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T *HostAdapter,
+					    unsigned char Value)
+{
+  outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_DataInRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T
+					     *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_InterruptRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_GeometryRegister);
+}
+
+
+/*
+  BusLogic_StartMailboxScan issues a Start Mailbox Scan command, which
+  notifies the Host Adapter that an entry has been made in an Outgoing
+  Mailbox.
+*/
+
+static inline
+void BusLogic_StartMailboxScan(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_WriteCommandParameterRegister(HostAdapter,
+					 BusLogic_StartMailboxCommand);
+}
+
+
+/*
+  BusLogic_Delay waits for Seconds to elapse.  It must be called with
+  interrupts enabled so that jiffies is updated.
+*/
+
+static inline void BusLogic_Delay(int Seconds)
+{
+  unsigned long TimeoutJiffies = jiffies + Seconds * HZ;
+  while (jiffies < TimeoutJiffies) ;
+}
+
+
+/*
+  Define prototypes for the forward referenced BusLogic Driver
+  Internal Functions.
+*/
+
+static void BusLogic_InterruptHandler(int, Registers_T *);
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *,
+				     SCSI_Command_T *);
+
+
+#endif /* BusLogic_DriverVersion */
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog
--- v1.3.41/linux/drivers/scsi/ChangeLog	Mon Sep 18 14:53:55 1995
+++ linux/drivers/scsi/ChangeLog	Tue Nov 14 10:01:16 1995
@@ -1,3 +1,7 @@
+Fri Nov 10 15:29:49 1995  Leonard N. Zubkoff (lnz@dandelion.com)
+
+	* Released new BusLogic driver.
+
 Wed Aug  9 22:37:04 1995  Andries Brouwer  <aeb@cwi.nl>
 
 	As a preparation for new device code, separated the various
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.41/linux/drivers/scsi/Config.in	Mon Oct 23 18:02:06 1995
+++ linux/drivers/scsi/Config.in	Tue Nov 14 10:02:51 1995
@@ -18,7 +18,7 @@
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
-dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
+bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC
 dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v1.3.41/linux/drivers/scsi/Makefile	Thu Nov  9 11:23:50 1995
+++ linux/drivers/scsi/Makefile	Tue Nov 14 10:01:16 1995
@@ -113,10 +113,10 @@
 endif
 
 ifeq ($(CONFIG_SCSI_BUSLOGIC),y)
-L_OBJS += buslogic.o
+L_OBJS += BusLogic.o
 else
   ifeq ($(CONFIG_SCSI_BUSLOGIC),m)
-  M_OBJS += buslogic.o
+  M_OBJS += BusLogic.o
   endif
 endif
 
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v1.3.41/linux/drivers/scsi/README.BusLogic	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/README.BusLogic	Tue Nov 14 03:21:19 1995
@@ -0,0 +1,262 @@
+		     BusLogic SCSI Driver for Linux 1.3.41
+		       Version 1.3.0 ~ 13 November 1995
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	   Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 INTRODUCTION
+
+BusLogic, Inc. designs and manufactures a variety of high performance SCSI host
+adapters which share a common programming interface across a diverse collection
+of bus architectures by virtue of their MultiMaster ASIC technology.  This
+driver supports all present BusLogic MultiMaster Host Adapters, and should
+support any future MultiMaster designs with little or no modification.  Host
+adapters based on the new FlashPoint technology are not supported by this
+driver.
+
+My primary goals in writing this completely new BusLogic driver for Linux are
+to achieve the full performance that BusLogic SCSI Host Adapters and modern
+SCSI peripherals are capable of, and to provide a highly robust driver that can
+be depended upon for high performance mission critical applications.  All of
+the major performance and error recovery features can be configured from the
+Linux kernel command line, allowing individual installations to tune driver
+performance and error recovery to their particular needs.
+
+The most recent versions of this driver will always be available by anonymous
+FTP from ftp.dandelion.com.  While directory listings are not permitted, the
+introductory banner displayed on anonymous FTP login will provide a list of the
+driver versions and any other files available for retrieval.
+
+Bug reports should be sent via electronic mail to "lnz@dandelion.com".  Please
+include with the bug report the complete configuration messages reported by the
+driver and SCSI subsystem at startup, along with any subsequent system messages
+relevant to SCSI operations, and a detailed description of your system's
+hardware configuration.
+
+I have recently had conversations with the Senior Product Marketing Manager at
+BusLogic regarding the needs of free software developers, and he has reaffirmed
+BusLogic's committment to providing the technical information and support we
+need to take full advantage of their products.  BusLogic has also been very
+accomodating in providing technical documentation, as well as access to their
+engineering staff for technical questions and advice.  In addition, they have
+loaned me ISA cards for configuration testing, and even allowed me use of their
+technical support lab to test EISA configurations, since I don't have an EISA
+system.  Their interest and support is greatly appreciated.
+
+BusLogic, Inc. is located at 4151 Burton Drive, Santa Clara, California, 95054,
+USA and can be reached by Voice at 408/492-9090 or by FAX at 408/492-1542.
+BusLogic maintains a World Wide Web site at http://www.buslogic.com, an
+anonymous FTP site at ftp.buslogic.com, and a BBS at 408/492-1984.  BusLogic
+Technical Support can be reached by electronic mail at techsup@buslogic.com, by
+Voice at 408/654-0760, or by FAX at 408/492-1542.  Contact information for
+offices in Europe and Japan is available on the Web site.
+
+
+				DRIVER FEATURES
+
+o Configuration Reporting and Testing
+
+  During system initialization, the driver reports extensively on the host
+  adapter hardware configuration, including the synchronous transfer parameters
+  negotiated with each target device.  In addition, the driver tests the
+  hardware interrupt configuration to verify that interrupts are actually
+  delivered correctly to the interrupt handler.  This should catch a high
+  percentage of PCI motherboard configuration errors early, because when the
+  host adapter is probed successfully, most of the remaining problems appear to
+  be related to interrupts.  Most often, any remaining hardware problems are
+  related to the specific configuration of devices on the SCSI bus, and the
+  quality of cabling and termination used.  Finally, this BusLogic driver
+  should never incorrectly attempt to support an Adaptec 154x Host Adapter.
+
+o Performance Features
+
+  BusLogic SCSI Host Adapters directly implement SCSI-2 Tagged Queuing, and so
+  support has been included in the driver to utilize tagged queuing with any
+  target devices that report having the tagged queuing capability.  Tagged
+  queuing allows for multiple outstanding commands to be issued to each target
+  device or logical unit, and can improve I/O performance substantially.  In
+  addition, BusLogic's Strict Round Robin Mode is used to optimize host adapter
+  performance, and scatter/gather I/O can support as many segments as can be
+  effectively utilized by the Linux I/O subsystem.  Control over the use of
+  tagged queuing for each target device as well as selection of the maximum
+  number of concurrent commands per logical unit is available from the kernel
+  command line.  In addition, tagged queuing is automatically disabled whenever
+  the host adapter firmware version is known not to implement it correctly, or
+  whenever a concurrency value of 1 is selected.  In performance testing,
+  sustained disk writes of 7.3MB per second have been observed to a /dev/sd
+  device.
+
+o Robustness Features
+
+  The driver implements extensive error recovery procedures.  By default, when
+  the higher level parts of the SCSI subsystem request that a command be reset,
+  a selection is made between a full host adapter hard reset and SCSI bus reset
+  versus sending a bus device reset message to the individual device based on
+  the recommendation of the SCSI subsystem.  Error recovery options are
+  selectable from the kernel command line individually for each target device,
+  and also include forcing a full host adapter hard reset and SCSI bus reset,
+  sending a bus device reset to the specific target device associated with the
+  command being reset, as well as suppressing error recovery entirely to avoid
+  perturbing an improperly functioning device.  If the bus device reset error
+  recovery option is selected and sending a bus device reset does not restore
+  correct operation, the next command that is reset will force a full host
+  adapter hard reset and SCSI bus reset.  SCSI bus resets caused by other
+  devices and detected by the host adapter are also handled by issuing a hard
+  reset to the host adapter and full reinitialization.  These error recovery
+  options should improve overall system robustness by preventing individual
+  errant devices from causing the system as a whole to lock up or crash, and
+  thereby allowing a clean shutdown and restart after the offending component
+  is removed.
+
+o Extensive Testing
+
+  This driver has undergone extensive testing and improvement over a period of
+  several months, and is routinely being used on heavily loaded systems.  Over
+  300 people retrieved the driver during the beta test period.  In addition to
+  testing in normal system operation, error recovery tests have been performed
+  to verify proper system recovery in the case of simulated dropped interrupts,
+  external SCSI bus resets, and SCSI command errors due to bad CD-ROM media.
+
+o PCI Configuration Support
+
+  On PCI systems running kernels compiled with PCI BIOS support enabled, this
+  driver will interrogate the PCI configuration space and use the I/O port
+  addresses assigned by the system BIOS, rather than the ISA compatible I/O
+  port addresses.
+
+o Shared Interrupts Support
+
+  On systems that support shared interrupts, any number of BusLogic Host
+  Adapters may share the same interrupt request channel, and in fact it is more
+  efficient if they do so.  The driver scans all known BusLogic Host Adapters
+  whenever an interrupt is handled on an interrupt channel assigned to any
+  BusLogic Host Adapter.
+
+o Wide SCSI Support
+
+  All BusLogic MultiMaster SCSI Host Adapters share a common programming
+  interface, except for the inevitable improvements and extensions as new
+  models are released, so support for Wide SCSI data transfer has automatically
+  been available without explicit driver support.  When used with Linux 1.3.x,
+  this driver adds explicit support for up to 15 target devices and 64 logical
+  units per target device, to fully exploit the capabilities of the newest
+  BusLogic Wide SCSI Host Adapters.
+
+
+			    SUPPORTED HOST ADAPTERS
+
+The following list comprises the supported BusLogic SCSI Host Adapters as of
+the date of this document.  It is recommended that anyone purchasing a BusLogic
+Host Adapter not in the following table contact the author beforehand to verify
+that it is or will be supported.
+
+"C" Series Host Adapters:
+
+946C	PCI	Fast Single-ended SCSI-2
+956C	PCI	Fast/Wide Single-ended SCSI-2
+956CD	PCI	Fast/Wide Differential SCSI-2
+445C	VLB	Fast Single-ended SCSI-2
+747C	EISA	Fast Single-ended SCSI-2
+757C	EISA	Fast/Wide Single-ended SCSI-2
+757CD	EISA	Fast/Wide Differential SCSI-2
+545C	ISA	Fast Single-ended SCSI-2
+540CF	ISA	Fast Single-ended SCSI-2
+
+"S" Series Host Adapters:
+
+445S	VLB	Fast Single-ended SCSI-2
+747S	EISA	Fast Single-ended SCSI-2
+747D	EISA	Fast Differential SCSI-2
+757S	EISA	Fast/Wide Single-ended SCSI-2
+757D	EISA	Fast/Wide Differential SCSI-2
+545S	ISA	Fast Single-ended SCSI-2
+542D	ISA	Fast Differential SCSI-2
+742A 	EISA	Single-ended SCSI-2 (742A revision H)
+542B	ISA	Single-ended SCSI-2 (542B revision H)
+
+"A" Series Host Adapters:
+
+742A 	EISA	Single-ended SCSI-2 (742A revisions A - G)
+542B	ISA	Single-ended SCSI-2 (542B revisions A - G)
+
+The FlashPoint LT, also known as the 930 Ultra, implements a different host
+interface and is not supported by this driver.
+
+AMI FastDisk Host Adapters are true BusLogic clones and are supported by this
+driver.
+
+
+			     COMMAND LINE OPTIONS
+
+Many features of this driver are configurable by specification of appropriate
+kernel command line options.  A full description of the command line options
+may be found in the comments before BusLogic_Setup in the kernel source code
+file "BusLogic.c".  The following examples may be useful as a starting point:
+
+  "BusLogic=0x330"
+
+    This command line limits probing to the single I/O port at 0x330.
+
+  "BusLogic=0,1"
+
+    This command line selects default probing and a concurrency of 1 which also
+    disables tagged queuing.  It may be useful if problems arise during
+    installation on a system with a flakey SCSI configuration.  In cases of a
+    marginal SCSI configuration it may also be beneficial to disable fast
+    transfers and/or synchronous negotiation using AutoSCSI on "C" series
+    boards.  Disconnect/reconnect may also be disabled for fast devices such as
+    disk drives, but should not be disabled for tape drives or other devices
+    where a single command may take over a second to execute.
+
+  "BusLogic=0,0,10"
+
+    This command line selects default probing and concurrency but changes the
+    bus settle time to 10 seconds.  It may be useful with SCSI devices that
+    take an unusually long time to become ready to accept commands after a SCSI
+    bus reset.
+
+  "BusLogic=TQ:Disable"
+
+    This command line selects default probing and disables tagged queuing,
+    while keeping the default concurrency.
+
+  "BusLogic=0,15,TQ:N"
+
+    This command line selects a concurrency of 15 and disables tagged queuing
+    for target 0, while allowing tagged queuing for all other target devices.
+
+Note that limiting the concurrency to 1 or disabling tagged queuing can
+substantially impact performance.
+
+
+				 INSTALLATION
+
+This distribution was prepared for Linux kernel version 1.3.41.  Installation
+in later versions will probably be successful as well, though BusLogic.patch
+may not be required once this driver becomes part of the standard development
+kernel; installation in earlier versions should not be attempted as 1.3.41
+contains changes I made to the common code that are essential for correct error
+recovery.
+
+To install the BusLogic SCSI driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
+
+  cd /usr/src
+  tar -xvzf BusLogic-1.3.0.tar.gz
+  mv README.BusLogic BusLogic.[ch] linux/drivers/scsi
+  patch -p < BusLogic.patch
+  cd linux
+  make config
+  make depend
+  make zImage
+
+Then install "arch/i386/boot/zImage" as your standard kernel, run lilo if
+appropriate, and reboot.
+
+Be sure to answer "y" to the "BusLogic SCSI support" query during the "make
+config" step.  If your system was already configured for the old BusLogic
+driver, you may omit the "make config" step above.
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c
--- v1.3.41/linux/drivers/scsi/buslogic.c	Mon Oct 23 18:02:10 1995
+++ linux/drivers/scsi/buslogic.c	Thu Jan  1 02:00:00 1970
@@ -1,1574 +0,0 @@
-/*
- *	buslogic.c	Copyright (C) 1993, 1994 David B. Gentzel
- *	Low-level scsi driver for BusLogic adapters
- *	by David B. Gentzel, Whitfield Software Services, Carnegie, PA
- *	    (gentzel@nova.enet.dec.com)
- *	Thanks to BusLogic for providing the necessary documentation
- *
- *	The original version of this driver was derived from aha1542.[ch],
- *	which is Copyright (C) 1992 Tommy Thorn.  Much has been reworked, but
- *	most of basic structure and substantial chunks of code still remain.
- *
- *	Furthermore, many subsequent fixes and improvements to the aha1542
- *	driver have been folded back into this driver.  These changes to
- *	aha1542.[ch] are Copyright (C) 1993, 1994 Eric Youngdale.
- *
- *	Thanks to the following individuals who have made contributions (of
- *	(code, information, support, or testing) to this driver:
- *		Eric Youngdale		Leonard Zubkoff
- *		Tomas Hurka		Andrew Walker
- */
-
-/*
- * TODO:
- *	1. Clean up error handling & reporting.
- *	2. Find out why scatter/gather is limited to 16 requests per command.
- *	3. Test/improve/fix abort & reset functions.
- *	4. Look at command linking.
- *	5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA,
- *	   and PCI).
- *	6. Avoid using the 445S workaround for board revs >= D.
- */
-
-/*
- * NOTES:
- *    BusLogic (formerly BusTek) manufactures an extensive family of
- *    intelligent, high performance SCSI-2 host adapters.  They all support
- *    command queueing and scatter/gather I/O.  Most importantly, they all
- *    support identical programming interfaces, so a single driver can be used
- *    for all boards.
- *
- *    Actually, they all support TWO identical programming interfaces!  They
- *    have an Adaptec 154x compatible interface (complete with 24 bit
- *    addresses) as well as a "native" 32 bit interface.  As such, the Linux
- *    aha1542 driver can be used to drive them, but with less than optimal
- *    performance (at least for the EISA, VESA, and MCA boards).
- *
- *    Here is the scoop on the various models:
- *	BT-542B - ISA first-party DMA with floppy support.
- *	BT-545S - 542B + FAST SCSI and active termination.
- *	BT-545D - 545S + differential termination.
- *	BT-640A - MCA bus-master with floppy support.
- *	BT-646S - 640A + FAST SCSI and active termination.
- *	BT-646D - 646S + differential termination.
- *	BT-742A - EISA bus-master with floppy support.
- *	BT-747S - 742A + FAST SCSI, active termination, and 2.88M floppy.
- *	BT-747D - 747S + differential termination.
- *	BT-757S - 747S + WIDE SCSI.
- *	BT-757D - 747D + WIDE SCSI.
- *	BT-445S - VESA bus-master FAST SCSI with active termination
- *		  and floppy support.
- *	BT-445C - 445S + enhanced BIOS & firmware options.
- *	BT-946C - PCI bus-master FAST SCSI.
- *	BT-956C - PCI bus-master FAST/WIDE SCSI.
- *
- *    ??? I believe other boards besides the 445 now have a "C" model, but I
- *    have no facts on them.
- *
- *    This driver SHOULD support all of these boards.  It has only been tested
- *    with a 747S, 445S, 946C, and 956C; there is no PCI-specific support as
- *    yet.
- *
- *    Should you require further information on any of these boards, BusLogic
- *    can be reached at (408)492-9090.  Their BBS # is (408)492-1984 (maybe BBS
- *    stands for "Big Brother System"?).
- *
- *    Places flagged with a triple question-mark are things which are either
- *    unfinished, questionable, or wrong.
- */
-
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/head.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/config.h>
-#include <linux/proc_fs.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/dma.h>
-
-#include <linux/blk.h>
-#include "scsi.h"
-#include "hosts.h"
-#include "sd.h"
-#define BUSLOGIC_PRIVATE_H	/* Get the "private" stuff */
-#include "buslogic.h"
-
-#ifndef BUSLOGIC_DEBUG
-# define BUSLOGIC_DEBUG 0
-#endif
-
-#include<linux/stat.h>
-
-struct proc_dir_entry proc_scsi_buslogic = {
-    PROC_SCSI_BUSLOGIC, 8, "buslogic",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-
-/* ??? Until kmalloc actually implements GFP_DMA, we can't depend on it... */
-#undef GFP_DMA
-
-/* If different port addresses are needed (e.g. to install more than two
-   cards), you must define BUSLOGIC_PORT_OVERRIDE to be a comma-separated list
-   of the addresses which will be checked.  This can also be used to resolve a
-   conflict if the port-probing at a standard port causes problems with
-   another board. */
-/* #define BUSLOGIC_PORT_OVERRIDE 0x330, 0x334, 0x130, 0x134, 0x230, 0x234 */
-
-/* Define this to be either BIOS_TRANSLATION_DEFAULT or BIOS_TRANSLATION_BIG
-   if you wish to bypass the test for this, which uses an undocumented port.
-   The test is believed to fail on at least some AMI BusLogic clones. */
-/* #define BIOS_TRANSLATION_OVERRIDE BIOS_TRANSLATION_BIG */
-
-#define BUSLOGIC_VERSION "1.15"
-
-/* Not a random value - if this is too large, the system hangs for a long time
-   waiting for something to happen if a board is not installed. */
-/* ??? I don't really like this as it will wait longer on slow machines.
-   Perhaps we should base this on the loops_per_second "Bogomips" value? */
-#define WAITNEXTTIMEOUT 3000000
-
-/* This is for the scsi_malloc call in buslogic_queuecommand. */
-/* ??? I'd up this to 4096, but would we be in danger of using up the
-   scsi_malloc memory pool? */
-/* This could be a concern, I guess.  It may be possible to fix things so that
-   the table generated in sd.c is compatible with the low-level code, but
-   don't hold your breath.  -ERY */
-#define BUSLOGIC_SG_MALLOC 512
-
-/* Since the SG list is malloced, we have to limit the length. */
-#define BUSLOGIC_MAX_SG (BUSLOGIC_SG_MALLOC / sizeof (struct chain))
-
-/* Since the host adapters have room to buffer 32 commands internally, there
-   is some virtue in setting BUSLOGIC_MAILBOXES to 32.  The maximum value
-   appears to be 255, since the Count parameter to the Initialize Extended
-   Mailbox command is limited to one byte. */
-#define BUSLOGIC_MAILBOXES 32
-
-#define BUSLOGIC_CMDLUN 4	/* Arbitrary, but seems to work well. */
-
-/* BusLogic boards can be configured for quite a number of port addresses (six
-   to be exact), but I generally do not want the driver poking around at
-   random.  We allow two port addresses - this allows people to use a BusLogic
-   with a MIDI card, which frequently also uses 0x330.
-
-   This can also be overridden on the command line to the kernel, via LILO or
-   LOADLIN. */
-static unsigned short bases[7] = {
-#ifdef BUSLOGIC_PORT_OVERRIDE
-    BUSLOGIC_PORT_OVERRIDE,
-#else
-    0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234, */
-#endif
-    0
-};
-
-#define BIOS_TRANSLATION_DEFAULT 0	/* Default case */
-#define BIOS_TRANSLATION_BIG 1		/* Big disk (> 1G) case */
-
-struct hostdata {
-    unsigned int bus_type;
-    unsigned int bios_translation: 1;	/* BIOS mapping (for compatibility) */
-    int last_mbi_used;
-    int last_mbo_used;
-    char model[7];
-    char firmware_rev[6];
-    Scsi_Cmnd *sc[BUSLOGIC_MAILBOXES];
-    struct mailbox mb[2 * BUSLOGIC_MAILBOXES];
-    struct ccb ccbs[BUSLOGIC_MAILBOXES];
-};
-
-#define HOSTDATA(host) ((struct hostdata *)&(host)->hostdata)
-
-/* One for each IRQ level (9-15), although 13 will never be used. */
-static struct Scsi_Host *host[7] = { NULL, };
-
-static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt);
-static int restart(struct Scsi_Host *shpnt);
-
-#define INTR_RESET(base) outb(RINT, CONTROL(base))
-
-#define buslogic_printk buslogic_prefix(__PRETTY_FUNCTION__),printk
-
-#if defined(MODULE) && !defined(GFP_DMA)
-# define CHECK_DMA_ADDR(isa, addr, badstmt) \
-    do { if ((isa) && ((const void *)addr) > (const void *)ISA_DMA_THRESHOLD) badstmt; } while (0)
-#else
-# define CHECK_DMA_ADDR(isa, addr, badstmt)
-#endif
-
-#define CHECK(cond) if (cond) ; else goto fail
-
-#define WAIT(port, allof, noneof) \
-    CHECK(wait(port, allof, noneof, WAITNEXTTIMEOUT, FALSE))
-#define WAIT_WHILE(port, mask) WAIT(port, 0, mask)
-#define WAIT_UNTIL(port, mask) WAIT(port, mask, 0)
-#define WAIT_FAST(port, allof, noneof) \
-    CHECK(wait(port, allof, noneof, 100, TRUE))
-#define WAIT_WHILE_FAST(port, mask) WAIT_FAST(port, 0, mask)
-#define WAIT_UNTIL_FAST(port, mask) WAIT_FAST(port, mask, 0)
-
-/* If delay != 0, we use the udelay call to regulate the amount of time we
-   wait.
-
-   This is inline as it is always called with constant arguments and hence
-   will be very well optimized. */
-static __inline__ int wait(unsigned short port,
-			   unsigned char allof, unsigned char noneof,
-			   unsigned int timeout, int delay)
-{
-    int bits;
-
-    for (;;) {
-	bits = inb(port);
-	if ((bits & allof) == allof && (bits & noneof) == 0)
-	    return TRUE;
-	if (delay)
-	    udelay(1000);
-	if (--timeout == 0)
-	    return FALSE;
-    }
-}
-
-static void buslogic_prefix(const char *func)
-{
-    printk("BusLogic SCSI: %s: ", func);
-}
-
-static void buslogic_stat(unsigned int base)
-{
-    int s = inb(STATUS(base)), i = inb(INTERRUPT(base));
-
-    buslogic_printk("status=%02X intrflags=%02X\n", s, i);
-}
-
-/* This is a bit complicated, but we need to make sure that an interrupt
-   routine does not send something out while we are in the middle of this.
-   Fortunately, it is only at boot time that multi-byte messages are ever
-   sent. */
-static int buslogic_out(unsigned int base, const unsigned char *cmdp,
-			size_t len)
-{
-    unsigned long flags = 0;
-    
-    if (len == 1) {
-	for (;;) {
-	    WAIT_WHILE(STATUS(base), CPRBSY);
-	    save_flags(flags);
-	    cli();
-	    if (!(inb(STATUS(base)) & CPRBSY)) {
-		outb(*cmdp, COMMAND_PARAMETER(base));
-		restore_flags(flags);
-		return FALSE;
-	    }
-	    restore_flags(flags);
-	}
-    } else {
-	save_flags(flags);
-	cli();
-	while (len--) {
-	    WAIT_WHILE(STATUS(base), CPRBSY);
-	    outb(*cmdp++, COMMAND_PARAMETER(base));
-	}
-	restore_flags(flags);
-    }
-    return FALSE;
-  fail:
-    restore_flags(flags);
-    buslogic_printk("failed(%u): ", len + 1);
-    buslogic_stat(base);
-    return TRUE;
-}
-
-/* Only used at boot time, so we do not need to worry about latency as much
-   here.  This waits a very short period of time.  We use this if we are not
-   sure whether the board will respond to the command we just sent. */
-static int buslogic_in(unsigned int base, unsigned char *cmdp, size_t len)
-{
-    unsigned long flags;
-    
-    save_flags(flags);
-    cli();
-    while (len--) {
-	WAIT_UNTIL_FAST(STATUS(base), DIRRDY);
-	*cmdp++ = inb(DATA_IN(base));
-    }
-    restore_flags(flags);
-    return FALSE;
-  fail:
-    restore_flags(flags);
-#if (BUSLOGIC_DEBUG & BD_IO)
-    buslogic_printk("failed(%u): ", len + 1);
-    buslogic_stat(base);
-#endif
-    return TRUE;
-}
-
-static unsigned int makecode(unsigned int haerr, unsigned int scsierr)
-{
-    unsigned int hosterr;
-    const char *errstr = NULL;
-#if (BUSLOGIC_DEBUG & BD_ERRORS) && defined(CONFIG_SCSI_CONSTANTS)
-    static const char *const buslogic_status[] = {
-    /* 00 */	"Command completed normally",
-    /* 01-07 */	NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    /* 08-09 */	NULL, NULL,
-    /* 0A */	"Linked command completed normally",
-    /* 0B */	"Linked command completed normally, interrupt generated",
-    /* 0C-0F */	NULL, NULL, NULL, NULL,
-    /* 10 */	NULL,
-    /* 11 */	"Selection timed out",
-    /* 12 */	"Data overrun/underrun",
-    /* 13 */	"Unexpected bus free",
-    /* 14 */	"Target bus phase sequence failure",
-    /* 15 */	"First byte of outgoing MB was invalid",
-    /* 16 */	"Invalid CCB Operation Code",
-    /* 17 */	"Linked CCB does not have the same LUN",
-    /* 18 */	"Invalid Target Direction received from Host",
-    /* 19 */	"Duplicate CCB Received in Target Mode",
-    /* 1A */	"Invalid CCB or Segment List Parameter",
-    /* 1B */	"Auto request sense failed",
-    /* 1C */	"SCSI-2 tagged queueing message was rejected by the target",
-    /* 1D-1F */	NULL, NULL, NULL,
-    /* 20 */	"Host adapter hardware failure",
-    /* 21 */	"Target did not respond to SCSI ATN and the HA SCSI bus reset",
-    /* 22 */	"Host adapter asserted a SCSI bus reset",
-    /* 23 */	"Other SCSI devices asserted a SCSI bus reset",
-    };
-#endif
-
-    switch (haerr) {
-      case 0x00:	/* Normal completion. */
-      case 0x0A:	/* Linked command complete without error and linked
-			   normally. */
-      case 0x0B:	/* Linked command complete without error, interrupt
-			   generated. */
-	hosterr = DID_OK;
-	break;
-
-      case 0x11:	/* Selection time out: the initiator selection or
-			   target reselection was not complete within the SCSI
-			   time out period. */
-	hosterr = DID_TIME_OUT;
-	break;
-
-      case 0x14:	/* Target bus phase sequence failure - An invalid bus
-			   phase or bus phase sequence was requested by the
-			   target.  The host adapter will generate a SCSI
-			   Reset Condition, notifying the host with a RSTS
-			   interrupt. */
-      case 0x21:	/* The target did not respond to SCSI ATN and the host
-			   adapter consequently issued a SCSI bus reset to
-			   clear up the failure. */
-      case 0x22:	/* The host adapter asserted a SCSI bus reset. */
-	hosterr = DID_RESET;
-	break;
-
-      case 0x12:	/* Data overrun/underrun: the target attempted to
-			   transfer more data than was allocated by the Data
-			   Length field or the sum of the Scatter/Gather Data
-			   Length fields. */
-      case 0x13:	/* Unexpected bus free - The target dropped the SCSI
-			   BSY at an unexpected time. */
-      case 0x15:	/* MBO command was not 00, 01, or 02 - The first byte
-			   of the MB was invalid.  This usually indicates a
-			   software failure. */
-      case 0x16:	/* Invalid CCB Operation Code - The first byte of the
-			   CCB was invalid.  This usually indicates a software
-			   failure. */
-      case 0x17:	/* Linked CCB does not have the same LUN - A
-			   subsequent CCB of a set of linked CCB's does not
-			   specify the same logical unit number as the
-			   first. */
-      case 0x18:	/* Invalid Target Direction received from Host - The
-			   direction of a Target Mode CCB was invalid. */
-      case 0x19:	/* Duplicate CCB Received in Target Mode - More than
-			   once CCB was received to service data transfer
-			   between the same target LUN and initiator SCSI ID
-			   in the same direction. */
-      case 0x1A:	/* Invalid CCB or Segment List Parameter - A segment
-			   list with a zero length segment or invalid segment
-			   list boundaries was received.  A CCB parameter was
-			   invalid. */
-      case 0x1B:	/* Auto request sense failed. */
-      case 0x1C:	/* SCSI-2 tagged queueing message was rejected by the
-			   target. */
-      case 0x20:	/* The host adapter hardware failed. */
-      case 0x23:	/* Other SCSI devices asserted a SCSI bus reset. */
-	hosterr = DID_ERROR;	/* ??? Couldn't find any better. */
-	break;
-
-      default:
-#ifndef CONFIG_SCSI_CONSTANTS
-	errstr = "unknown hoststatus";
-#endif
-	hosterr = DID_ERROR;
-	break;
-    }
-#if (BUSLOGIC_DEBUG & BD_ERRORS)
-# ifdef CONFIG_SCSI_CONSTANTS
-    if (hosterr != DID_OK) {
-	if (haerr < ARRAY_SIZE(buslogic_status))
-	    errstr = buslogic_status[haerr];
-	if (errstr == NULL)
-	    errstr = "unknown hoststatus";
-    }
-# else
-    if (hosterr == DID_ERROR)
-	errstr = "";
-# endif
-#endif
-    if (errstr != NULL)
-	buslogic_printk("%s (%02X)\n", errstr, haerr);
-    return (hosterr << 16) | scsierr;
-}
-
-/* ??? this should really be "const struct Scsi_Host *" */
-const char *buslogic_info(struct Scsi_Host *shpnt)
-{
-    return "BusLogic SCSI driver " BUSLOGIC_VERSION;
-}
-
-/*
-  This is a major rewrite of the interrupt handler to support the newer
-  and faster PCI cards.  While the previous interrupt handler was supposed
-  to handle multiple incoming becoming available mailboxes during the same
-  interrupt, my testing showed that in practice only a single mailbox was
-  ever made available.  With the 946C and 956C, multiple incoming mailboxes
-  being ready for processing during a single interrupt occurs much more
-  frequently, and so care must be taken to avoid race conditions managing
-  the Host Adapter Interrupt Register, which can lead to lost interrupts.
-
-  Leonard N. Zubkoff, 23-Mar-95
-*/
-
-static void buslogic_interrupt(int irq, struct pt_regs * regs)
-{
-    int mbi, saved_mbo[BUSLOGIC_MAILBOXES];
-    int base, interrupt_flags, found, i;
-    struct Scsi_Host *shpnt;
-    Scsi_Cmnd *sctmp;
-    struct mailbox *mb;
-    struct ccb *ccb;
-
-    shpnt = host[irq - 9];
-    if (shpnt == NULL)
-      panic("buslogic_interrupt: NULL SCSI host entry");
-
-    mb = HOSTDATA(shpnt)->mb;
-    ccb = HOSTDATA(shpnt)->ccbs;
-    base = shpnt->io_port;
-
-    /*
-      This interrupt handler is now specified to use the SA_INTERRUPT
-      protocol, so interrupts are inhibited on entry until explicitly
-      allowed again.  Read the Host Adapter Interrupt Register, and
-      complain if there is no pending interrupt being signaled.
-    */
-
-    interrupt_flags = inb(INTERRUPT(base));
-
-    if (!(interrupt_flags & INTV))
-      buslogic_printk("interrupt received, but INTV not set\n");
-
-    /*
-      Reset the Host Adapter Interrupt Register.  It appears to be
-      important that this is only done once per interrupt to avoid
-      losing interrupts under heavy loads.
-    */
-
-    INTR_RESET(base);
-
-    if (interrupt_flags & RSTS)
-      {
-	restart(shpnt);
-	return;
-      }
-
-    /*
-      With interrupts still inhibited, scan through the incoming mailboxes
-      in strict round robin fashion saving the status information and
-      then freeing the mailbox.  A second pass over the completed commands
-      will be made separately to complete their processing.
-    */
-
-    mbi = HOSTDATA(shpnt)->last_mbi_used + 1;
-    if (mbi >= 2*BUSLOGIC_MAILBOXES)
-      mbi = BUSLOGIC_MAILBOXES;
-
-    found = 0;
-
-    while (mb[mbi].status != MBX_NOT_IN_USE && found < BUSLOGIC_MAILBOXES)
-      {
-	int mbo = (struct ccb *)mb[mbi].ccbptr - ccb;
-
-	sctmp = HOSTDATA(shpnt)->sc[mbo];
-
-	/*
-	  If sctmp has become NULL, higher level code must have aborted
-	  this operation and called the necessary completion routine.
-	*/
-
-	if (sctmp != NULL && mb[mbi].status != MBX_COMPLETION_NOT_FOUND)
-	  {
-	    int result = 0;
-
-	    saved_mbo[found++] = mbo;
-
-	    if (mb[mbi].status != MBX_COMPLETION_OK)
-	      result = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
-
-	    sctmp->result = result;
-
-	    mb[mbi].status = MBX_NOT_IN_USE;
-	  }
-
-	HOSTDATA(shpnt)->last_mbi_used = mbi;
-
-	if (++mbi >= 2*BUSLOGIC_MAILBOXES)
-	  mbi = BUSLOGIC_MAILBOXES;
-      }
-
-    /*
-      With interrupts no longer inhibited, iterate over the completed
-      commands freeing resources and calling the completion routines.
-      Since we exit upon completion of this loop, there is no need to
-      inhibit interrupts before exit, as this will be handled by the
-      fast interrupt assembly code we return to.
-    */
-
-    sti();
-
-    for (i = 0; i < found; i++)
-      {
-	int mbo = saved_mbo[i];
-	sctmp = HOSTDATA(shpnt)->sc[mbo];
-	if (sctmp == NULL) continue;
-	/*
-	  First, free any storage allocated for a scatter/gather
-	  data segment list.
-	*/
-	if (sctmp->host_scribble)
-	  scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
-	/*
-	  Next, mark the SCSI Command as completed so it may be reused
-	  for another command by buslogic_queuecommand.  This also signals
-	  to buslogic_reset that the command is no longer active.
-	*/
-	HOSTDATA(shpnt)->sc[mbo] = NULL;
-	/*
-	  Finally, call the SCSI command completion handler.
-	*/
-	sctmp->scsi_done(sctmp);
-      }
-}
-
-
-/* ??? Why does queuecommand return a value?  scsi.c never looks at it... */
-int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
-{
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    unsigned char direction;
-    unsigned char *cmd = (unsigned char *)scpnt->cmnd;
-    unsigned char target = scpnt->target;
-    unsigned char lun = scpnt->lun;
-    void *buff = scpnt->request_buffer;
-    int bufflen = scpnt->request_bufflen;
-    int mbo;
-    unsigned long flags;
-    struct Scsi_Host *shpnt = scpnt->host;
-    struct mailbox *mb = HOSTDATA(shpnt)->mb;
-    struct ccb *ccb;
-
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    if (target > 1) {
-	scpnt->result = DID_TIME_OUT << 16;
-	done(scpnt);
-	return 0;
-    }
-#endif
-
-    if (*cmd == REQUEST_SENSE) {
-#if (BUSLOGIC_DEBUG & (BD_COMMAND | BD_ERRORS))
-	if (bufflen != sizeof scpnt->sense_buffer) {
-	    buslogic_printk("wrong buffer length supplied for request sense"
-			    " (%d).\n",
-			    bufflen);
-	}
-#endif
-	scpnt->result = 0;
-	done(scpnt);
-	return 0;
-    }
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    {
-	int i;
-
-	if (*cmd == READ_10 || *cmd == WRITE_10
-	    || *cmd == READ_6 || *cmd == WRITE_6)
-	    i = *(int *)(cmd + 2);
-	else
-	    i = -1;
-	buslogic_printk("dev %d cmd %02X pos %d len %d ",
-			target, *cmd, i, bufflen);
-	buslogic_stat(shpnt->io_port);
-	buslogic_printk("dumping scsi cmd:");
-	for (i = 0; i < scpnt->cmd_len; i++)
-	    printk(" %02X", cmd[i]);
-	printk("\n");
-	if (*cmd == WRITE_10 || *cmd == WRITE_6)
-	    return 0;	/* we are still testing, so *don't* write */
-    }
-#endif
-
-    /* Use the outgoing mailboxes in a round-robin fashion, because this
-       is how the host adapter will scan for them. */
-
-    save_flags(flags);
-    cli();
-
-    mbo = HOSTDATA(shpnt)->last_mbo_used + 1;
-    if (mbo >= BUSLOGIC_MAILBOXES)
-	mbo = 0;
-
-    do {
-	if (mb[mbo].status == MBX_NOT_IN_USE
-	    && HOSTDATA(shpnt)->sc[mbo] == NULL)
-	    break;
-	mbo++;
-	if (mbo >= BUSLOGIC_MAILBOXES)
-	    mbo = 0;
-    } while (mbo != HOSTDATA(shpnt)->last_mbo_used);
-
-    if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(shpnt)->sc[mbo]) {
-	/* ??? Instead of failing, should we enable OMBR interrupts and sleep
-	   until we get one? */
-	restore_flags(flags);
-	buslogic_printk("unable to find empty mailbox.\n");
-	goto fail;
-    }
-
-    HOSTDATA(shpnt)->sc[mbo] = scpnt;		/* This will effectively
-						   prevent someone else from
-						   screwing with this cdb. */
-
-    HOSTDATA(shpnt)->last_mbo_used = mbo;
-
-    restore_flags(flags);
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    buslogic_printk("sending command (%d %08X)...", mbo, done);
-#endif
-
-    ccb = &HOSTDATA(shpnt)->ccbs[mbo];
-
-    /* This gets trashed for some reason */
-    mb[mbo].ccbptr = ccb;
-
-    memset(ccb, 0, sizeof (struct ccb));
-
-    ccb->cdblen = scpnt->cmd_len;		/* SCSI Command Descriptor
-						   Block Length */
-
-    direction = 0;
-    if (*cmd == READ_10 || *cmd == READ_6)
-	direction = 8;
-    else if (*cmd == WRITE_10 || *cmd == WRITE_6)
-	direction = 16;
-
-    memcpy(ccb->cdb, cmd, ccb->cdblen);
-
-    if (scpnt->use_sg) {
-	struct scatterlist *sgpnt;
-	struct chain *cptr;
-	size_t i;
-
-	ccb->op = CCB_OP_INIT_SG;	/* SCSI Initiator Command
-					   w/scatter-gather */
-	scpnt->host_scribble
-	    = (unsigned char *)scsi_malloc(BUSLOGIC_SG_MALLOC);
-	if (scpnt->host_scribble == NULL) {
-	    buslogic_printk("unable to allocate DMA memory.\n");
-	    goto fail;
-	}
-	sgpnt = (struct scatterlist *)scpnt->request_buffer;
-	cptr = (struct chain *)scpnt->host_scribble;
-	if (scpnt->use_sg > shpnt->sg_tablesize) {
-	    buslogic_printk("bad segment list, %d > %d.\n",
-			    scpnt->use_sg, shpnt->sg_tablesize);
-	    goto fail;
-	}
-	for (i = 0; i < scpnt->use_sg; i++) {
-	    CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, sgpnt[i].address,
-			   goto baddma);
-	    cptr[i].dataptr = sgpnt[i].address;
-	    cptr[i].datalen = sgpnt[i].length;
-	}
-	ccb->datalen = scpnt->use_sg * sizeof (struct chain);
-	ccb->dataptr = cptr;
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-	{
-	    unsigned char *ptr;
-
-	    buslogic_printk("cptr %08X:", cptr);
-	    ptr = (unsigned char *)cptr;
-	    for (i = 0; i < 18; i++)
-		printk(" %02X", ptr[i]);
-	    printk("\n");
-	}
-#endif
-    } else {
-	ccb->op = CCB_OP_INIT;	/* SCSI Initiator Command */
-	scpnt->host_scribble = NULL;
-	CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, buff, goto baddma);
-	ccb->datalen = bufflen;
-	ccb->dataptr = buff;
-    }
-    ccb->id = target;
-    ccb->lun = lun;
-    ccb->dir = direction;
-    ccb->rsalen = sizeof scpnt->sense_buffer;
-    ccb->senseptr = scpnt->sense_buffer;
-    /* ccbcontrol, commlinkid, and linkptr are 0 due to above memset. */
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    {
-	size_t i;
-
-	buslogic_printk("sending...");
-	for (i = 0; i < sizeof(struct ccb) - 10; i++)
-	    printk(" %02X", ((unsigned char *)ccb)[i]);
-	printk("\n");
-    }
-#endif
-
-    if (done) {
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-	buslogic_printk("now waiting for interrupt: ");
-	buslogic_stat(shpnt->io_port);
-#endif
-	scpnt->scsi_done = done;
-	mb[mbo].status = MBX_ACTION_START;
-	/* start scsi command */
-	buslogic_out(shpnt->io_port, buscmd, sizeof buscmd);
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-	buslogic_stat(shpnt->io_port);
-#endif
-    } else
-	buslogic_printk("done can't be NULL.\n");
-
-    while (0) {
-#if defined(MODULE) && !defined(GFP_DMA)
-      baddma:
-	buslogic_printk("address > 16MB used for ISA HA.\n");
-#endif
-      fail:
-	scpnt->result = DID_ERROR << 16;
-	done(scpnt);
-    }
-
-    return 0;
-}
-
-#if 0
-static void internal_done(Scsi_Cmnd *scpnt)
-{
-    scpnt->SCp.Status++;
-}
-
-int buslogic_command(Scsi_Cmnd *scpnt)
-{
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    buslogic_printk("calling buslogic_queuecommand.\n");
-#endif
-
-    buslogic_queuecommand(scpnt, internal_done);
-
-    scpnt->SCp.Status = 0;
-    while (!scpnt->SCp.Status)
-	barrier();
-    return scpnt->result;
-}
-#endif
-
-/* Initialize mailboxes. */
-static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt)
-{
-    size_t i;
-    int ok = FALSE;		/* Innocent until proven guilty... */
-    struct mailbox *mb = HOSTDATA(shpnt)->mb;
-    struct ccb *ccb = HOSTDATA(shpnt)->ccbs;
-    struct {
-	unsigned char cmd, count;
-	void *base PACKED;
-    } cmd = { CMD_INITEXTMB, BUSLOGIC_MAILBOXES, mb };
-
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++) {
-	mb[i].status = mb[BUSLOGIC_MAILBOXES + i].status = MBX_NOT_IN_USE;
-	mb[i].ccbptr = &ccb[i];
-    }
-    INTR_RESET(base);	/* reset interrupts, so they don't block */
-
-    if (buslogic_out(base, (unsigned char *)&cmd, sizeof cmd))
-	goto fail;
-    WAIT_UNTIL(INTERRUPT(base), CMDC);
-
-    ok = TRUE;
-
-    while (0) {
-      fail:
-	buslogic_printk("failed setting up mailboxes.\n");
-    }
-
-    INTR_RESET(base);
-
-    return !ok;
-}
-
-static int getconfig(unsigned int base, unsigned char *irq,
-		     unsigned char *dma, unsigned char *id,
-		     char *bus_type, unsigned short *max_sg,
-		     const unsigned char **bios)
-{
-    unsigned char inquiry_cmd[2];
-    unsigned char inquiry_result[4];
-    int i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    i = inb(STATUS(base));
-    if (i & DIRRDY)
-	i = inb(DATA_IN(base));
-    inquiry_cmd[0] = CMD_RETCONF;
-    buslogic_out(base, inquiry_cmd, 1);
-    if (buslogic_in(base, inquiry_result, 3))
-	goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    INTR_RESET(base);
-    /* Defer using the DMA value until we know the bus type. */
-    *dma = inquiry_result[0];
-    switch (inquiry_result[1]) {
-      case 0x01:
-	*irq = 9;
-	break;
-      case 0x02:
-	*irq = 10;
-	break;
-      case 0x04:
-	*irq = 11;
-	break;
-      case 0x08:
-	*irq = 12;
-	break;
-      case 0x20:
-	*irq = 14;
-	break;
-      case 0x40:
-	*irq = 15;
-	break;
-      default:
-	buslogic_printk("unable to determine BusLogic IRQ level, "
-			" disabling board.\n");
-	goto fail;
-    }
-    *id = inquiry_result[2] & 0x7;
-
-    /* I expected Adaptec boards to fail on this, but it doesn't happen... */
-    inquiry_cmd[0] = CMD_INQEXTSETUP;
-    inquiry_cmd[1] = 4;
-    if (buslogic_out(base, inquiry_cmd, 2))
-	goto fail;
-    if (buslogic_in(base, inquiry_result, inquiry_cmd[1]))
-	goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    if (inb(STATUS(base)) & CMDINV)
-	goto fail;
-    INTR_RESET(base);
-
-    *bus_type = inquiry_result[0];
-    CHECK(*bus_type == 'A' || *bus_type == 'E' || *bus_type == 'M');
-
-    *bios = (const unsigned char *)((unsigned int)inquiry_result[1] << 12);
-
-    *max_sg = (inquiry_result[3] << 8) | inquiry_result[2];
-
-    /* We only need a DMA channel for ISA boards.  Some other types of boards
-       (such as the 747S) have an option to report a DMA channel even though
-       none is used (for compatibility with Adaptec drivers which require a
-       DMA channel).  We ignore this. */
-    if (*bus_type == 'A')
-	switch (*dma) {
-	  case 0:	/* This indicates that no DMA channel is used. */
-	    *dma = 0;
-	    break;
-	  case 0x20:
-	    *dma = 5;
-	    break;
-	  case 0x40:
-	    *dma = 6;
-	    break;
-	  case 0x80:
-	    *dma = 7;
-	    break;
-	  default:
-	    buslogic_printk("unable to determine BusLogic DMA channel,"
-			    " disabling board.\n");
-	    goto fail;
-	}
-    else
-	*dma = 0;
-
-    while (0) {
-      fail:
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-	buslogic_printk("query board settings\n");
-#endif
-	return TRUE;
-    }
-
-    return FALSE;
-}
-
-/* Query the board.  This acts both as part of the detection sequence and as a
-   means to get necessary configuration information. */
-static int buslogic_query(unsigned int base, unsigned char *trans,
-			  unsigned char *irq, unsigned char *dma,
-			  unsigned char *id, char *bus_type,
-			  unsigned short *max_sg, const unsigned char **bios,
-			  char *model, char *firmware_rev)
-{
-    unsigned char inquiry_cmd[2];
-    unsigned char inquiry_result[6];
-    unsigned char geo;
-    unsigned int i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    /* Quick and dirty test for presence of the card. */
-    if (inb(STATUS(base)) == 0xFF)
-	goto fail;
-
-    /* Check the GEOMETRY port early for quick bailout on Adaptec boards. */
-    geo = inb(GEOMETRY(base));
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("geometry bits: %02X\n", geo);
-#endif
-    /* Here is where we tell the men from the boys (i.e. Adaptec's don't
-       support the GEOMETRY port, the men do :-) */
-    if (geo == 0xFF)
-	goto fail;
-
-    /* In case some other card was probing here, reset interrupts. */
-    INTR_RESET(base);
-
-    /* Reset the adapter.  I ought to make a hard reset, but it's not really
-       necessary. */
-    outb(RSOFT | RINT/* | RSBUS*/, CONTROL(base));
-
-    /* Wait a little bit for things to settle down. */
-    i = jiffies + 2;
-    while (i > jiffies);
-
-    /* Expect INREQ and HARDY, any of the others are bad. */
-    WAIT(STATUS(base), INREQ | HARDY, DACT | DFAIL | CMDINV | DIRRDY | CPRBSY);
-
-    /* Shouldn't have generated any interrupts during reset. */
-    if (inb(INTERRUPT(base)) & INTRMASK)
-	goto fail;
-
-    /* Getting the BusLogic firmware revision level is a bit tricky.  We get
-       the first two digits (d.d) from CMD_INQUIRY and then use two undocumented
-       commands to get the remaining digit and letter (d.ddl as in 3.31C). */
-
-    inquiry_cmd[0] = CMD_INQUIRY;
-    buslogic_out(base, inquiry_cmd, 1);
-    if (buslogic_in(base, inquiry_result, 4))
-	goto fail;
-    /* Reading port should reset DIRRDY. */
-    if (inb(STATUS(base)) & DIRRDY)
-	goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    INTR_RESET(base);
-    firmware_rev[0] = inquiry_result[2];
-    firmware_rev[1] = '.';
-    firmware_rev[2] = inquiry_result[3];
-    firmware_rev[3] = '\0';
-#if 0
-    buslogic_printk("inquiry bytes: %02X(%c) %02X(%c)\n",
-		    inquiry_result[0], inquiry_result[0],
-		    inquiry_result[1], inquiry_result[1]);
-#endif
-
-    if (getconfig(base, irq, dma, id, bus_type, max_sg, bios))
-	goto fail;
-
-    /* Set up defaults */
-#ifdef BIOS_TRANSLATION_OVERRIDE
-    *trans = BIOS_TRANSLATION_OVERRIDE;
-#else
-    *trans = BIOS_TRANSLATION_DEFAULT;
-#endif
-    model[0] = '\0';
-    model[6] = 0;
-
-    /* ??? Begin undocumented command use.
-       These may not be supported by clones. */
-
-    do {
-	/* ??? It appears as though AMI BusLogic clones don't implement this
-	   feature.  As an experiment, if we read a 00 we ignore the GEO_GT_1GB
-	   bit and skip all further undocumented commands. */
-	if (geo == 0x00)
-	    break;
-#ifndef BIOS_TRANSLATION_OVERRIDE
-	*trans = ((geo & GEO_GT_1GB)
-		  ? BIOS_TRANSLATION_BIG : BIOS_TRANSLATION_DEFAULT);
-#endif
-
-	inquiry_cmd[0] = CMD_VER_NO_LAST;
-	buslogic_out(base, inquiry_cmd, 1);
-	if (buslogic_in(base, inquiry_result, 1))
-	    break;
-	WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-	INTR_RESET(base);
-	firmware_rev[3] = inquiry_result[0];
-	firmware_rev[4] = '\0';
-
-	inquiry_cmd[0] = CMD_VER_NO_LETTER;
-	buslogic_out(base, inquiry_cmd, 1);
-	if (buslogic_in(base, inquiry_result, 1))
-	    break;
-	WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-	INTR_RESET(base);
-	firmware_rev[4] = inquiry_result[0];
-	firmware_rev[5] = '\0';
-
-	/* Use undocumented command to get model number and revision. */
-
-	inquiry_cmd[0] = CMD_RET_MODEL_NO;
-	inquiry_cmd[1] = 6;
-	buslogic_out(base, inquiry_cmd, 2);
-	if (buslogic_in(base, inquiry_result, inquiry_cmd[1]))
-	    break;
-	WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-	INTR_RESET(base);
-	memcpy(model, inquiry_result, 5);
-	model[5] = '\0';
-	model[6] = inquiry_result[5];
-    } while (0);
-
-    /* ??? End undocumented command use. */
-
-    /* bus_type from getconfig doesn't differentiate between EISA/VESA.  We
-       override using the model number here. */
-    switch (*bus_type) {
-      case 'E':
-	switch (model[0]) {
-	  case '4':
-	    *bus_type = 'V';
-	    break;
-	  case '9':
-	    *bus_type = 'P';
-	    break;
-	  case '7':
-	    break;
-	  default:
-	    *bus_type = 'X';
-	    break;
-	}
-	break;
-      default:
-	break;
-    }
-
-    while (0) {
-      fail:
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-	buslogic_printk("query board settings\n");
-#endif
-	return TRUE;
-    }
-
-    return FALSE;
-}
-
-/* return non-zero on detection */
-int buslogic_detect(Scsi_Host_Template *tpnt)
-{
-    unsigned char dma;
-    unsigned char irq;
-    unsigned int base;
-    unsigned char id;
-    char bus_type;
-    unsigned short max_sg;
-    unsigned char bios_translation;
-    unsigned long flags;
-    const unsigned char *bios;
-    char *model;
-    char *firmware_rev;
-    struct Scsi_Host *shpnt;
-    size_t indx;
-    int unchecked_isa_dma;
-    int count = 0;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    tpnt->proc_dir = &proc_scsi_buslogic; 
-    tpnt->can_queue = BUSLOGIC_MAILBOXES;
-    for (indx = 0; bases[indx] != 0; indx++)
-	if (!check_region(bases[indx], 4)) {
-	    shpnt = scsi_register(tpnt, sizeof (struct hostdata));
-
-	    base = bases[indx];
-
-	    model = HOSTDATA(shpnt)->model;
-	    firmware_rev = HOSTDATA(shpnt)->firmware_rev;
-	    if (buslogic_query(base, &bios_translation, &irq, &dma, &id,
-			       &bus_type, &max_sg, &bios, model, firmware_rev))
-		goto unregister;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-	    buslogic_stat(base);
-#endif
-
-	    /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */
-	    unchecked_isa_dma = (bus_type == 'A');
-#ifndef CONFIG_NO_BUGGY_BUSLOGIC
-	    /* There is a hardware bug in the BT-445S prior to revision D.
-	       When the BIOS is enabled and you have more than 16MB of memory,
-	       the card mishandles memory transfers over 16MB which (if viewed
-	       as a 24-bit address) overlap with the BIOS address space.  For
-	       example if you have the BIOS located at physical address
-	       0xDC000 and a DMA transfer from the card to RAM starts at
-	       physical address 0x10DC000 then the transfer is messed up.  To
-	       be more precise every fourth byte of the transfer is messed up.
-	       (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP
-	       BusLogic driver.) */
-
-	    if (bus_type == 'V'				    /* 445 */
-		&& firmware_rev[0] <= '3'		    /* S */
-		&& bios != NULL) {			    /* BIOS enabled */
-#if 1
-		/* Now that LNZ's forbidden_addr stuff is in the higher level
-		   scsi code, we can use this instead. */
-		/* Avoid addresses which "mirror" the BIOS for DMA. */
-		shpnt->forbidden_addr = (unsigned long)bios;
-		shpnt->forbidden_size = 16 * 1024;
-#else
-		/* Use double-buffering. */
-		unchecked_isa_dma = TRUE;
-#endif
-	    }
-#endif
-
-	    CHECK_DMA_ADDR(unchecked_isa_dma, shpnt, goto unregister);
-
-	    if (setup_mailboxes(base, shpnt))
-		goto unregister;
-
-	    /* Set the Bus on/off-times as not to ruin floppy performance.
-	       CMD_BUSOFF_TIME is a noop for EISA boards (and possibly
-	       others???). */
-	    if (bus_type != 'E' && bus_type != 'P') {
-		/* The default ON/OFF times for BusLogic adapters is 7/4. */
-		static const unsigned char oncmd[] = { CMD_BUSON_TIME, 7 };
-		static const unsigned char offcmd[] = { CMD_BUSOFF_TIME, 5 };
-
-		INTR_RESET(base);
-		buslogic_out(base, oncmd, sizeof oncmd);
-		WAIT_UNTIL(INTERRUPT(base), CMDC);
-		INTR_RESET(base);
-		buslogic_out(base, offcmd, sizeof offcmd);
-		WAIT_UNTIL(INTERRUPT(base), CMDC);
-		while (0) {
-		  fail:
-		    buslogic_printk("setting bus on/off-time failed.\n");
-		}
-		INTR_RESET(base);
-	    }
-
-	    buslogic_printk("configuring %s HA at port 0x%03X, IRQ %u",
-			    (bus_type == 'A' ? "ISA"
-			     : (bus_type == 'E' ? "EISA"
-				: (bus_type == 'M' ? "MCA"
-				   : (bus_type == 'P' ? "PCI"
-				      : (bus_type == 'V' ? "VESA"
-					 : (bus_type == 'X' ? "EISA/VESA/PCI"
-					    : "Unknown")))))),
-			    base, irq);
-	    if (bios != NULL)
-		printk(", BIOS 0x%05X", (unsigned int)bios);
-	    if (dma != 0)
-		printk(", DMA %u", dma);
-	    printk(", ID %u\n", id);
-	    buslogic_printk("Model Number: %s",
-			    (model[0] ? model : "Unknown"));
-	    if (model[0])
-		printk(" (revision %d)", model[6]);
-	    printk("\n");
-	    buslogic_printk("firmware revision: %s\n", firmware_rev);
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-	    buslogic_stat(base);
-#endif
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-	    buslogic_printk("enable interrupt channel %d.\n", irq);
-#endif
-
-	    save_flags(flags);
-	    cli();
-	    if (request_irq(irq, buslogic_interrupt,
-			    SA_INTERRUPT, "buslogic")) {
-		buslogic_printk("unable to allocate IRQ for "
-				"BusLogic controller.\n");
-		restore_flags(flags);
-		goto unregister;
-	    }
-
-	    if (dma) {
-		if (request_dma(dma, "buslogic")) {
-		    buslogic_printk("unable to allocate DMA channel for "
-				    "BusLogic controller.\n");
-		    free_irq(irq);
-		    restore_flags(flags);
-		    goto unregister;
-		}
-
-		/* The DMA-Controller.  We need to fool with this because we
-		   want to be able to use an ISA BusLogic without having to
-		   have the BIOS enabled. */
-		set_dma_mode(dma, DMA_MODE_CASCADE);
-		enable_dma(dma);
-	    }
-
-	    host[irq - 9] = shpnt;
-	    shpnt->this_id = id;
-	    shpnt->unchecked_isa_dma = unchecked_isa_dma;
-	    /* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots
-	       of memory gets sucked up for bounce buffers.  */
-	    shpnt->cmd_per_lun = (unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN);
-	    shpnt->sg_tablesize = max_sg;
-	    if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG)
-		shpnt->sg_tablesize = BUSLOGIC_MAX_SG;
-	    /* ??? shpnt->base should really be "const unsigned char *"... */
-	    shpnt->base = (unsigned char *)bios;
-	    shpnt->io_port = base;
-	    shpnt->n_io_port = 4;	/* Number of bytes of I/O space used */
-	    shpnt->dma_channel = dma;
-	    shpnt->irq = irq;
-	    HOSTDATA(shpnt)->bios_translation = bios_translation;
-	    if (bios_translation == BIOS_TRANSLATION_BIG)
-		buslogic_printk("using extended bios translation.\n");
-	    HOSTDATA(shpnt)->last_mbi_used = 2 * BUSLOGIC_MAILBOXES - 1;
-	    HOSTDATA(shpnt)->last_mbo_used = BUSLOGIC_MAILBOXES - 1;
-	    memset(HOSTDATA(shpnt)->sc, 0, sizeof HOSTDATA(shpnt)->sc);
-	    restore_flags(flags);
-
-#if 0
-	    {
-		unsigned char buf[8];
-		unsigned char cmd[]
-		    = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-		size_t i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-		buslogic_printk("*** READ CAPACITY ***\n");
-#endif
-		for (i = 0; i < sizeof buf; i++)
-		    buf[i] = 0x87;
-		for (i = 0; i < 2; i++)
-		    if (!buslogic_command(i, cmd, buf, sizeof buf)) {
-			buslogic_printk("LU %u sector_size %d device_size %d\n",
-					i, *(int *)(buf + 4), *(int *)buf);
-		    }
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-		buslogic_printk("*** NOW RUNNING MY OWN TEST ***\n");
-#endif
-		for (i = 0; i < 4; i++) {
-		    static buffer[512];
-
-		    cmd[0] = READ_10;
-		    cmd[1] = 0;
-		    xany2scsi(cmd + 2, i);
-		    cmd[6] = 0;
-		    cmd[7] = 0;
-		    cmd[8] = 1;
-		    cmd[9] = 0;
-		    buslogic_command(0, cmd, buffer, sizeof buffer);
-		}
-	    }
-#endif
-
-	    request_region(bases[indx], 4,"buslogic");
-	    /* Register the IO ports that we use */
-	    count++;
-	    continue;
-	  unregister:
-	    scsi_unregister(shpnt);
-	}
-    return count;
-}
-
-static int restart(struct Scsi_Host *shpnt)
-{
-    unsigned int i;
-    unsigned int count = 0;
-#if 0
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-#endif
-
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-	if (HOSTDATA(shpnt)->sc[i]
-	    && !HOSTDATA(shpnt)->sc[i]->device->soft_reset) {
-#if 0
-	    HOSTDATA(shpnt)->mb[i].status
-		= MBX_ACTION_START;	/* Indicate ready to restart... */
-#endif
-	    count++;
-	}
-
-    buslogic_printk("potential to restart %d stalled commands...\n", count);
-#if 0
-    /* start scsi command */
-    if (count)
-	buslogic_out(shpnt->host->io_port, buscmd, sizeof buscmd);
-#endif
-    return 0;
-}
-
-/* ??? The abort command for the aha1542 does not leave the device in a clean
-   state where it is available to be used again.  As it is not clear whether
-   the same problem exists with BusLogic boards, we will enable this and see
-   if it works. */
-int buslogic_abort(Scsi_Cmnd *scpnt)
-{
-#if 1
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    struct mailbox *mb;
-    int mbi, mbo, last_mbi;
-    unsigned long flags;
-    unsigned int i;
-
-    buslogic_printk("%X %X\n",
-		    inb(STATUS(scpnt->host->io_port)),
-		    inb(INTERRUPT(scpnt->host->io_port)));
-
-    save_flags(flags);
-    cli();
-    mb = HOSTDATA(scpnt->host)->mb;
-    last_mbi = HOSTDATA(scpnt->host)->last_mbi_used;
-    mbi = last_mbi + 1;
-    if (mbi >= 2 * BUSLOGIC_MAILBOXES)
-	mbi = BUSLOGIC_MAILBOXES;
-
-    do {
-	if (mb[mbi].status != MBX_NOT_IN_USE)
-	    break;
-	last_mbi = mbi;
-	mbi++;
-	if (mbi >= 2 * BUSLOGIC_MAILBOXES)
-	    mbi = BUSLOGIC_MAILBOXES;
-    } while (mbi != HOSTDATA(scpnt->host)->last_mbi_used);
-
-    if (mb[mbi].status != MBX_NOT_IN_USE) {
-	buslogic_printk("lost interrupt discovered on irq %d, "
-			" - attempting to recover...\n",
-			scpnt->host->irq);
-	HOSTDATA(scpnt->host)->last_mbi_used = last_mbi;
-	buslogic_interrupt(scpnt->host->irq, NULL);
-	restore_flags(flags);
-	return SCSI_ABORT_SUCCESS;
-    }
-    restore_flags(flags);
-
-    /* OK, no lost interrupt.  Try looking to see how many pending commands we
-       think we have. */
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-	if (HOSTDATA(scpnt->host)->sc[i]) {
-	    if (HOSTDATA(scpnt->host)->sc[i] == scpnt) {
-		buslogic_printk("timed out command pending for %s.\n",
-			kdevname(scpnt->request.rq_dev));
-		if (HOSTDATA(scpnt->host)->mb[i].status != MBX_NOT_IN_USE) {
-		    buslogic_printk("OGMB still full - restarting...\n");
-		    buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-		}
-	    } else
-		buslogic_printk("other pending command: %s\n",
-			kdevname(scpnt->request.rq_dev));
-	}
-#endif
-
-#if (BUSLOGIC_DEBUG & BD_ABORT)
-    buslogic_printk("called\n");
-#endif
-
-#if 1
-    /* This section of code should be used carefully - some devices cannot
-       abort a command, and this merely makes it worse. */
-    save_flags(flags);
-    cli();
-    for (mbo = 0; mbo < BUSLOGIC_MAILBOXES; mbo++)
-	if (scpnt == HOSTDATA(scpnt->host)->sc[mbo]) {
-	    mb[mbo].status = MBX_ACTION_ABORT;
-	    buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-	    break;
-	}
-    restore_flags(flags);
-#endif
-
-    return SCSI_ABORT_SNOOZE;
-}
-
-/* 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.  For a first go, we assume
-   that the BusLogic notifies us with all of the pending commands (it does
-   implement soft reset, after all). */
-int buslogic_reset(Scsi_Cmnd *scpnt)
-{
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    unsigned int i;
-
-#if (BUSLOGIC_DEBUG & BD_RESET)
-    buslogic_printk("called\n");
-#endif
-#if 0
-    /* This does a scsi reset for all devices on the bus. */
-    outb(RSBUS, CONTROL(scpnt->host->io_port));
-#else
-    /* This does a selective reset of just the one device. */
-    /* First locate the ccb for this command. */
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-	if (HOSTDATA(scpnt->host)->sc[i] == scpnt) {
-	    HOSTDATA(scpnt->host)->ccbs[i].op = CCB_OP_BUS_RESET;
-
-	    /* Now tell the BusLogic to flush all pending commands for this
-	       target. */
-	    buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-
-	    /* Here is the tricky part.  What to do next.  Do we get an
-	       interrupt for the commands that we aborted with the specified
-	       target, or do we generate this on our own?  Try it without first
-	       and see what happens. */
-	    buslogic_printk("sent BUS DEVICE RESET to target %d.\n",
-			    scpnt->target);
-
-	    /* If the first does not work, then try the second.  I think the
-	       first option is more likely to be correct.  Free the command
-	       block for all commands running on this target... */
-#if 1
-	    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-		if (HOSTDATA(scpnt->host)->sc[i]
-		    && HOSTDATA(scpnt->host)->sc[i]->target == scpnt->target) {
-		    Scsi_Cmnd *sctmp = HOSTDATA(scpnt->host)->sc[i];
-
-		    sctmp->result = DID_RESET << 16;
-		    if (sctmp->host_scribble)
-			scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
-		    buslogic_printk("sending DID_RESET for target %d.\n",
-				    scpnt->target);
-		    sctmp->scsi_done(scpnt);
-
-		    HOSTDATA(scpnt->host)->sc[i] = NULL;
-		    HOSTDATA(scpnt->host)->mb[i].status = MBX_NOT_IN_USE;
-		}
-	    return SCSI_RESET_SUCCESS;
-#else
-	    return SCSI_RESET_PENDING;
-#endif
-	}
-#endif
-    /* No active command at this time, so this means that each time we got some
-       kind of response the last time through.  Tell the mid-level code to
-       request sense information in order to decide what to do next. */
-    return SCSI_RESET_PUNT;
-}
-
-/* ??? This is probably not correct for series "C" boards.  I believe these
-   support separate mappings for each disk.  We would need to issue a
-   CMD_READ_FW_LOCAL_RAM command to check for the particular drive being
-   queried.  Note that series "C" boards can be differentiated by having
-   HOSTDATA(disk->device->host)->firmware_rev[0] >= '4'. */
-int buslogic_biosparam(Disk *disk, kdev_t dev, int *ip)
-{
-    unsigned int size = disk->capacity;
-
-    /* ip[0] == heads, ip[1] == sectors, ip[2] == cylinders */
-    if (HOSTDATA(disk->device->host)->bios_translation == BIOS_TRANSLATION_BIG
-	&& size >= 0x200000) {		/* 1GB */
-	if (size >= 0x400000) {		/* 2GB */
-#if 0	/* ??? Used in earlier kernels, but disagrees with BusLogic info. */
-	    if (mb >= 0x800000) {	/* 4GB */
-		ip[0] = 256;
-		ip[1] = 64;
-	    } else {
-		ip[0] = 256;
-		ip[1] = 32;
-	    }
-#else
-	    ip[0] = 255;
-	    ip[1] = 63;
-#endif
-	} else {
-	    ip[0] = 128;
-	    ip[1] = 32;
-	}
-    } else {
-	ip[0] = 64;
-	ip[1] = 32;
-    }
-    ip[2] = size / (ip[0] * ip[1]);
-/*    if (ip[2] > 1024)
-	ip[2] = 1024; */
-    return 0;
-}
-
-/* called from init/main.c */
-void buslogic_setup(char *str, int *ints)
-{
-    static const unsigned short valid_bases[]
-	= { 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 };
-    static size_t setup_idx = 0;
-    size_t i;
-
-    if (setup_idx >= ARRAY_SIZE(bases) - 1) {
-	buslogic_printk("called too many times.  Bad LILO params?\n");
-	return;
-    }
-    if (ints[0] != 1) {
-	buslogic_printk("malformed command line.\n");
-	buslogic_printk("usage: buslogic=<portbase>\n");
-	return;
-    }
-    for (i = 0; i < ARRAY_SIZE(valid_bases); i++)
-	if (valid_bases[i] == ints[1]) {
-	    bases[setup_idx++] = ints[1];
-	    bases[setup_idx] = 0;
-	    return;
-	}
-    buslogic_printk("invalid base 0x%X specified.\n", ints[1]);
-}
-
-#ifdef MODULE
-/* Eventually this will go into an include file, but that's later... */
-Scsi_Host_Template driver_template = BUSLOGIC;
-
-# include "scsi_module.c"
-#endif
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/buslogic.h linux/drivers/scsi/buslogic.h
--- v1.3.41/linux/drivers/scsi/buslogic.h	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/buslogic.h	Thu Jan  1 02:00:00 1970
@@ -1,190 +0,0 @@
-/*
- *	buslogic.h	Copyright (C) 1993, 1994 David B. Gentzel
- *	See buslogic.c for more information.
- */
-
-#ifndef _BUSLOGIC_H
-
-int buslogic_detect(Scsi_Host_Template *);
-int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int buslogic_abort(Scsi_Cmnd *);
-const char *buslogic_info(struct Scsi_Host *);
-int buslogic_reset(Scsi_Cmnd *);
-int buslogic_biosparam(Disk *, kdev_t, int *);
-
-
-#define BUSLOGIC { NULL, NULL,			\
-		   NULL,		        \
-		   NULL,		        \
-		   "BusLogic",			\
-		   buslogic_detect,		\
-		   0,	/* no release func */	\
-		   buslogic_info,		\
-		   0,	/* no command func */	\
-		   buslogic_queuecommand,	\
-		   buslogic_abort,		\
-		   buslogic_reset,		\
-		   0,	/* slave_attach NYI */	\
-		   buslogic_biosparam,		\
-		   0,	/* set by driver */	\
-		   0,	/* set by driver */	\
-		   0,	/* set by driver */	\
-		   0,	/* set by driver */	\
-		   0,				\
-		   0,	/* set by driver */	\
-		   ENABLE_CLUSTERING		\
-		 }
-
-#ifdef BUSLOGIC_PRIVATE_H
-
-/* ??? These don't really belong here */
-#ifndef TRUE
-# define TRUE 1
-#endif
-#ifndef FALSE
-# define FALSE 0
-#endif
-
-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
-
-#define PACKED __attribute__((packed))
-
-#define BD_ABORT	0x0001
-#define BD_COMMAND	0x0002
-#define BD_DETECT	0x0004
-#define BD_ERRORS	0x0008
-#define BD_INTERRUPT	0x0010
-#define BD_IO		0x0020
-#define BD_RESET	0x0040
-#define BD_UNDOCUMENTED	0x0080
-
-/* I/O Port interface */
-/* READ */
-#define STATUS(base) (base)
-#define DACT 0x80		/* Diagnostic Active */
-#define DFAIL 0x40		/* Diagnostic Failure */
-#define INREQ 0x20		/* Initialization Required */
-#define HARDY 0x10		/* Host Adapter Ready */
-#define CPRBSY 0x08		/* Command/Parameter Register Busy */
-#define DIRRDY 0x04		/* Data In Register Ready */
-				/* 0x02 is reserved */
-#define CMDINV 0x01		/* Command Invalid */
-
-#define DATA_IN(base) (STATUS(base) + 1)
-
-#define INTERRUPT(base) (STATUS(base) + 2)
-#define INTV 0x80		/* Interrupt Valid */
-				/* 0x70 are reserved */
-#define RSTS 0x08		/* SCSI Reset State */
-#define CMDC 0x04		/* Command Complete */
-#define MBOR 0x02		/* Mailbox Out Ready */
-#define IMBL 0x01		/* Incoming Mailbox Loaded */
-#define INTRMASK 0x8F
-
-/* This undocumented port returns a bitmask indicating geometry translation. */
-#define GEOMETRY(base) (STATUS(base) + 3)
-#define GEO_GT_1GB 0x80		/* > 1GB under DOS geometry mapping */
-				/* 0x70 are unknown */
-#define GEO_XLATION_S_D1 0x0C	/* Disk 1 geometry ("S" models only) */
-#define GEO_XLATION_S_D0 0x03	/* Disk 0 geometry ("S" models only) */
-
-
-/* WRITE */
-#define CONTROL(base) STATUS(base)
-#define RHARD 0x80		/* Hard Reset */
-#define RSOFT 0x40		/* Soft Reset */
-#define RINT 0x20		/* Interrupt Reset */
-#define RSBUS 0x10		/* SCSI Bus Reset */
-				/* 0x0F are reserved */
-
-#define COMMAND_PARAMETER(base) (STATUS(base) + 1)
-#define CMD_TSTCMDCINT 0x00	/* Test CMDC Interrupt */
-#define CMD_INITMB 0x01		/* Initialize Mailbox */
-#define CMD_START_SCSI 0x02	/* Start Mailbox */
-#define CMD_START_BIOS 0x03	/* Start BIOS */
-#define CMD_INQUIRY 0x04	/* Inquire Board ID */
-#define CMD_ENBOMBRINT 0x05	/* Enable OMBR Interrupt */
-#define CMD_SETSELTIMOUT 0x06	/* Set SCSI Selection Time-Out */
-#define CMD_BUSON_TIME 0x07	/* Set Bus-On Time */
-#define CMD_BUSOFF_TIME 0x08	/* Set Bus-Off Time */
-#define CMD_BUSXFR_RATE 0x09	/* Set Bus Transfer Rate */
-#define CMD_INQ_DEVICES 0x0A	/* Inquire Installed Devices */
-#define CMD_RETCONF 0x0B	/* Return Configuration */
-#define CMD_TARGET_MODE 0x0C	/* Set Target Mode */
-#define CMD_INQ_SETUP_INFO 0x0D	/* Inquire Set-up Information */
-#define CMD_WRITE_LCL_RAM 0x1A	/* Write Adapter Local RAM */
-#define CMD_READ_LCL_RAM 0x1B	/* Read Adapter Local RAM */
-#define CMD_WRITE_BM_FIFO 0x1C	/* Write Bus Master Chip FIFO */
-#define CMD_READ_BM_FIFO 0x1D	/* Read Bus Master Chip FIFO */
-#define CMD_ECHO 0x1F		/* Echo Data Byte */
-#define CMD_HA_DIAG 0x20	/* Host Adapter Diagnostic */
-#define CMD_HA_OPTIONS 0x21	/* Host Adapter Options */
-#define CMD_INITEXTMB 0x81	/* Initialize Extended Mailbox */
-#define CMD_VER_NO_LAST 0x84	/* Version Number Last Byte (undocumented) */
-#define CMD_VER_NO_LETTER 0x85	/* Version Number One Letter (undocumented) */
-#define CMD_RET_MODEL_NO 0x8B	/* Return Model Number (undocumented) */
-#define CMD_INQEXTSETUP 0x8D	/* Inquire Extended Set-up Information */
-#define CMD_ROUND_ROBIN 0x8F	/* Enable strict vs. half-assed round-robin
-				   mailbox filling (undocumented) */
-#define CMD_READ_FW_LCL_RAM 0x91/* Read Firmware Local RAM (undocumented) */
-#define CMD_WRITE_INQ_BUF 0x9A	/* Write Inquiry Data Buffer
-				   (Target Mode Only) */
-#define CMD_READ_INQ_BUF 0x9B	/* Read Inquiry Data Buffer
-				   (Target Mode Only) */
-
-#define MBX_NOT_IN_USE 0x00
-#define MBX_ACTION_START 0x01
-#define MBX_ACTION_ABORT 0x02
-#define MBX_COMPLETION_OK 0x01
-#define MBX_COMPLETION_ABORTED 0x02
-#define MBX_COMPLETION_NOT_FOUND 0x03
-#define MBX_COMPLETION_ERROR 0x04
-
-/* Mailbox Definition */
-struct mailbox {
-    void *ccbptr;		/* lsb, ..., msb */
-    unsigned char btstat;
-    unsigned char sdstat;
-    unsigned char reserved;
-    unsigned char status;	/* Command/Status */
-};
-
-/* This is used with scatter-gather */
-struct chain {
-    unsigned long datalen;	/* Size of this part of chain */
-    void *dataptr;		/* Location of data */
-};
-
-#define MAX_CDB 12
-
-struct ccb {			/* Command Control Block */
-    unsigned char op;		/* Command Control Block Operation Code */
-    unsigned char dir;
-    unsigned char cdblen;	/* SCSI Command Length */
-    unsigned char rsalen;	/* Request Sense Allocation Length/Disable */
-    unsigned long datalen;	/* Data Length (msb, ..., lsb) */
-    void *dataptr;		/* Data Pointer */
-    unsigned char reserved[2];
-    unsigned char hastat;	/* Host Adapter Status (HASTAT) */
-    unsigned char tarstat;	/* Target Device Status */
-    unsigned char id;
-    unsigned char lun;
-    unsigned char cdb[MAX_CDB];
-    unsigned char ccbcontrol;
-    unsigned char commlinkid;	/* Command Linking Identifier */
-    void *linkptr;		/* Link Pointer */
-    void *senseptr;
-};
-
-#define CCB_OP_INIT 0x00	/* Initiator CCB */
-#define CCB_OP_TARG 0x01	/* Target CCB */
-#define CCB_OP_INIT_SG 0x02	/* Initiator CCB with scatter-gather */
-#define CCB_OP_INIT_R 0x03	/* Initiator CCB with residual data length
-				   returned */
-#define CCB_OP_INIT_SG_R 0x04	/* Initiator CCB with scatter-gather and
-				   residual data length returned */
-#define CCB_OP_BUS_RESET 0x81	/* SCSI bus device reset */
-
-#endif
-
-#endif
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.41/linux/drivers/scsi/hosts.c	Sat Nov 11 17:41:33 1995
+++ linux/drivers/scsi/hosts.c	Tue Nov 14 10:01:16 1995
@@ -58,7 +58,7 @@
 #endif
 
 #ifdef CONFIG_SCSI_BUSLOGIC
-#include "buslogic.h"
+#include "BusLogic.h"
 #endif
 
 #ifdef CONFIG_SCSI_EATA_DMA
@@ -167,7 +167,7 @@
 #ifdef CONFIG_SCSI_AHA152X
     AHA152X,
 #endif
-/* Buslogic must come before aha1542.c */
+/* BusLogic must come before aha1542.c */
 #ifdef CONFIG_SCSI_BUSLOGIC
     BUSLOGIC,
 #endif
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.41/linux/drivers/scsi/scsi_syms.c	Sat Nov 11 17:41:33 1995
+++ linux/drivers/scsi/scsi_syms.c	Tue Nov 14 07:35:31 1995
@@ -53,6 +53,8 @@
     X(scsi_ioctl),
     X(print_command),
     X(print_sense),
+    X(print_msg),
+    X(print_status),
     X(dma_free_sectors),
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.3.41/linux/drivers/scsi/seagate.c	Mon Oct 23 18:02:12 1995
+++ linux/drivers/scsi/seagate.c	Tue Nov 14 08:41:22 1995
@@ -406,6 +406,33 @@
 	return buffer;
 }
 
+int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
+                               int length, int hostno, int inout)
+{
+       const char *info = seagate_st0x_info(NULL);
+       int len;
+       int pos;
+       int begin;
+
+       if (inout) return(-ENOSYS);
+
+       begin = 0;
+       strcpy(buffer,info);
+       strcat(buffer,"\n");
+
+       pos = len = strlen(buffer);
+
+       if (pos<offset) {
+               len = 0;
+               begin = pos;
+               }
+
+       *start = buffer + (offset - begin);
+       len -= (offset - begin);
+       if ( len > length ) len = length;
+       return(len);
+}
+
 /*
  * These are our saved pointers for the outstanding command that is 
  * waiting for a reconnect
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h
--- v1.3.41/linux/drivers/scsi/seagate.h	Mon Sep 18 14:54:03 1995
+++ linux/drivers/scsi/seagate.h	Tue Nov 14 08:44:04 1995
@@ -19,6 +19,7 @@
 int seagate_st0x_abort(Scsi_Cmnd *);
 const char *seagate_st0x_info(struct Scsi_Host *);
 int seagate_st0x_reset(Scsi_Cmnd *); 
+int seagate_st0x_proc_info(char *,char **,off_t,int,int,int);
 
 #ifndef NULL
 	#define NULL 0
@@ -27,7 +28,7 @@
 #include <linux/kdev_t.h>
 int seagate_st0x_biosparam(Disk *, kdev_t, int*);
 
-#define SEAGATE_ST0X  {  NULL, NULL, NULL, NULL, \
+#define SEAGATE_ST0X  {  NULL, NULL, NULL, seagate_st0x_proc_info, \
 			 NULL, seagate_st0x_detect, 	\
 			 NULL, 						\
 			 seagate_st0x_info, seagate_st0x_command,  	\
diff -u --recursive --new-file v1.3.41/linux/fs/Config.in linux/fs/Config.in
--- v1.3.41/linux/fs/Config.in	Mon Oct 23 18:02:17 1995
+++ linux/fs/Config.in	Tue Nov 14 16:03:52 1995
@@ -15,6 +15,9 @@
 bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
+  if [ "$CONFIG_NFS_FS" = "y" ]; then
+    bool 'Root file system on NFS' CONFIG_ROOT_NFS
+  fi
 fi
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
diff -u --recursive --new-file v1.3.41/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v1.3.41/linux/fs/binfmt_elf.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/binfmt_elf.c	Tue Nov 14 17:06:55 1995
@@ -345,8 +345,8 @@
 #define INTERPRETER_ELF 2
 
 
-static int
-load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static inline int
+do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct elfhdr elf_ex;
 	struct elfhdr interp_elf_ex;
@@ -370,8 +370,6 @@
 	unsigned int elf_stack;
 	char passed_fileno[6];
 	
-	MOD_INC_USE_COUNT;
-
 	ibcs2_interpreter = 0;
 	status = 0;
 	load_addr = 0;
@@ -379,7 +377,6 @@
 	
 	if (elf_ex.e_ident[0] != 0x7f ||
 	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-		MOD_DEC_USE_COUNT;
 		return  -ENOEXEC;
 	}
 	
@@ -390,7 +387,6 @@
 	   (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
 	   (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
 	    !bprm->inode->i_op->default_file_ops->mmap)){
-		MOD_DEC_USE_COUNT;
 		return -ENOEXEC;
 	}
 	
@@ -399,7 +395,6 @@
 	elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * 
 						 elf_ex.e_phnum, GFP_KERNEL);
 	if (elf_phdata == NULL) {
-		MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	
@@ -407,7 +402,6 @@
 			   elf_ex.e_phentsize * elf_ex.e_phnum, 1);
 	if (retval < 0) {
 		kfree (elf_phdata);
-		MOD_DEC_USE_COUNT;
 		return retval;
 	}
 	
@@ -420,7 +414,6 @@
 
 	if (elf_exec_fileno < 0) {
 		kfree (elf_phdata);
-		MOD_DEC_USE_COUNT;
 		return elf_exec_fileno;
 	}
 	
@@ -438,7 +431,6 @@
 			{
 				kfree (elf_phdata);
 				kfree(elf_interpreter);
-				MOD_DEC_USE_COUNT;
 				return -EINVAL;
 			}
 
@@ -451,7 +443,6 @@
 							   GFP_KERNEL);
 			if (elf_interpreter == NULL) {
 				kfree (elf_phdata);
-				MOD_DEC_USE_COUNT;
 				return -ENOMEM;
 			}
 			
@@ -484,7 +475,6 @@
 			if(retval < 0) {
 				kfree (elf_phdata);
 				kfree(elf_interpreter);
-				MOD_DEC_USE_COUNT;
 				return retval;
 			}
 		}
@@ -509,7 +499,6 @@
 		  {
 		    kfree(elf_interpreter);
 		    kfree(elf_phdata);
-		    MOD_DEC_USE_COUNT;
 		    return -ELIBBAD;
 		  }
 	}
@@ -534,7 +523,6 @@
 			      kfree(elf_interpreter);
 			}
 			kfree (elf_phdata);
-			MOD_DEC_USE_COUNT;
 			return -E2BIG;
 		}
 	}
@@ -587,7 +575,6 @@
 		    printk("Unable to load interpreter\n");
 		    kfree(elf_phdata);
 		    send_sig(SIGSEGV, current, 0);
-		    MOD_DEC_USE_COUNT;
 		    return 0;
 		  }
 		}
@@ -709,15 +696,25 @@
 	start_thread(regs, elf_entry, bprm->p);
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
+static int
+load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+	retval = do_load_elf_binary(bprm, regs);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+
 /* This is really simpleminded and specialized - we are loading an
    a.out library that is given an ELF header. */
 
-static int
-load_elf_library(int fd){
+static inline int
+do_load_elf_library(int fd){
 	struct file * file;
 	struct elfhdr elf_ex;
 	struct elf_phdr *elf_phdata  =  NULL;
@@ -729,47 +726,46 @@
 	int error;
 	int i,j, k;
 
-	MOD_INC_USE_COUNT;
 	len = 0;
 	file = current->files->fd[fd];
 	inode = file->f_inode;
 	elf_bss = 0;
 	
-	set_fs(KERNEL_DS);
-	if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) {
-		SYS(close)(fd);
-		MOD_DEC_USE_COUNT;
+	if (!file || !file->f_op)
 		return -EACCES;
-	}
+
+	/* seek to the beginning of the file */
+	if (file->f_op->lseek) {
+		if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
+			return -ENOEXEC;
+	} else
+		file->f_pos = 0;
+
+	set_fs(KERNEL_DS);
+	error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex));
 	set_fs(USER_DS);
-	
+	if (error != sizeof(elf_ex))
+		return -ENOEXEC;
+
 	if (elf_ex.e_ident[0] != 0x7f ||
-	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-		MOD_DEC_USE_COUNT;
+	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
 		return -ENOEXEC;
-	}
-	
+
 	/* First of all, some simple consistency checks */
 	if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
 	   (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
-	   (!inode->i_op || !inode->i_op->default_file_ops->mmap)){
-		MOD_DEC_USE_COUNT;
+	   (!inode->i_op || !inode->i_op->default_file_ops->mmap))
 		return -ENOEXEC;
-	}
 	
 	/* Now read in all of the header information */
 	
-	if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) {
-		MOD_DEC_USE_COUNT;
+	if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE)
 		return -ENOEXEC;
-	}
 	
 	elf_phdata =  (struct elf_phdr *) 
 		kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
-	if (elf_phdata == NULL) {
-		MOD_DEC_USE_COUNT;
+	if (elf_phdata == NULL)
 		return -ENOMEM;
-	}
 	
 	retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
 			   sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
@@ -780,7 +776,6 @@
 	
 	if(j != 1)  {
 		kfree(elf_phdata);
-		MOD_DEC_USE_COUNT;
 		return -ENOEXEC;
 	}
 	
@@ -800,7 +795,6 @@
 	SYS(close)(fd);
 	if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
 		kfree(elf_phdata);
-		MOD_DEC_USE_COUNT;
 		return error;
 	}
 
@@ -813,10 +807,19 @@
 		  PROT_READ|PROT_WRITE|PROT_EXEC,
 		  MAP_FIXED|MAP_PRIVATE, 0);
 	kfree(elf_phdata);
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
+static int load_elf_library(int fd)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+	retval = do_load_elf_library(fd);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+	
 /*
  * ELF core dumper
  *
diff -u --recursive --new-file v1.3.41/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.41/linux/fs/buffer.c	Mon Oct 23 18:02:17 1995
+++ linux/fs/buffer.c	Tue Nov 14 08:46:23 1995
@@ -428,7 +428,7 @@
 		bh->b_next->b_prev = bh;
 }
 
-static struct buffer_head * find_buffer(kdev_t dev, int block, int size)
+static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
 {		
 	struct buffer_head * tmp;
 
@@ -807,10 +807,8 @@
 /*
  * Release a buffer head
  */
-void brelse(struct buffer_head * buf)
+void __brelse(struct buffer_head * buf)
 {
-	if (!buf)
-		return;
 	wait_on_buffer(buf);
 
 	/* If dirty, mark the time this buffer should be written back */
@@ -828,10 +826,8 @@
 /*
  * bforget() is like brelse(), except is throws the buffer away
  */
-void bforget(struct buffer_head * buf)
+void __bforget(struct buffer_head * buf)
 {
-	if (!buf)
-		return;
 	wait_on_buffer(buf);
 	if (buf->b_count != 1) {
 		printk("Aieee... bforget(): count = %d\n", buf->b_count);
@@ -1044,7 +1040,7 @@
  * "address" points to the new page we can use to move things
  * around..
  */
-static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
+static inline unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
 	unsigned long address)
 {
 	while (nrbuf-- > 0)
diff -u --recursive --new-file v1.3.41/linux/fs/dcache.c linux/fs/dcache.c
--- v1.3.41/linux/fs/dcache.c	Mon Sep 18 14:54:05 1995
+++ linux/fs/dcache.c	Tue Nov 14 08:41:44 1995
@@ -130,7 +130,7 @@
 /*
  * Find a directory cache entry given all the necessary info.
  */
-static struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
+static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
 {
 	struct dir_cache_entry * de = hash->next;
 
diff -u --recursive --new-file v1.3.41/linux/fs/exec.c linux/fs/exec.c
--- v1.3.41/linux/fs/exec.c	Sat Nov 11 17:41:33 1995
+++ linux/fs/exec.c	Tue Nov 14 11:31:06 1995
@@ -953,12 +953,22 @@
 	file = current->files->fd[fd];
 	inode = file->f_inode;
 	
-	set_fs(KERNEL_DS);
-	if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
+	if (!file || !file->f_op)
 		return -EACCES;
-	}
+
+	/* Seek into the file */
+	if (file->f_op->lseek) {
+		if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
+			return -ENOEXEC;
+	} else
+		file->f_pos = 0;
+
+	set_fs(KERNEL_DS);
+	error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
 	set_fs(USER_DS);
-	
+	if (error != sizeof(ex))
+		return -ENOEXEC;
+
 	/* We come in here for the regular a.out style of shared libraries */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
 	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
diff -u --recursive --new-file v1.3.41/linux/fs/ext/inode.c linux/fs/ext/inode.c
--- v1.3.41/linux/fs/ext/inode.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/ext/inode.c	Tue Nov 14 08:42:00 1995
@@ -171,7 +171,7 @@
 
 #define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)])
 
-static int block_bmap(struct buffer_head * bh, int nr)
+static inline int block_bmap(struct buffer_head * bh, int nr)
 {
 	int tmp;
 
diff -u --recursive --new-file v1.3.41/linux/fs/fcntl.c linux/fs/fcntl.c
--- v1.3.41/linux/fs/fcntl.c	Fri Jun  2 15:56:27 1995
+++ linux/fs/fcntl.c	Tue Nov 14 08:41:48 1995
@@ -17,7 +17,7 @@
 extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
 extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
 
-static int dupfd(unsigned int fd, unsigned int arg)
+static inline int dupfd(unsigned int fd, unsigned int arg)
 {
 	if (fd >= NR_OPEN || !current->files->fd[fd])
 		return -EBADF;
diff -u --recursive --new-file v1.3.41/linux/fs/file_table.c linux/fs/file_table.c
--- v1.3.41/linux/fs/file_table.c	Tue Aug  8 12:31:39 1995
+++ linux/fs/file_table.c	Tue Nov 14 08:41:52 1995
@@ -19,7 +19,7 @@
 /*
  * Insert a new file structure at the head of the list of available ones.
  */
-static void insert_file_free(struct file *file)
+static inline void insert_file_free(struct file *file)
 {
 	file->f_count = 0;
 	file->f_next = first_file;
@@ -32,7 +32,7 @@
 /*
  * Remove a file structure from the list of available ones.
  */
-static void remove_file_free(struct file *file)
+static inline void remove_file_free(struct file *file)
 {
 	if (first_file == file)
 		first_file = first_file->f_next;
@@ -44,7 +44,7 @@
 /*
  * Insert a file structure at the end of the list of available ones.
  */
-static void put_last_free(struct file *file)
+static inline void put_last_free(struct file *file)
 {
 	file->f_prev = first_file->f_prev;
 	file->f_prev->f_next = file;
diff -u --recursive --new-file v1.3.41/linux/fs/filesystems.c linux/fs/filesystems.c
--- v1.3.41/linux/fs/filesystems.c	Fri Jul  7 08:54:51 1995
+++ linux/fs/filesystems.c	Thu Nov 16 08:54:24 1995
@@ -21,9 +21,15 @@
 #include <linux/sysv_fs.h>
 #include <linux/hpfs_fs.h>
 #include <linux/smb_fs.h>
+#include <linux/major.h>
 
 extern void device_setup(void);
 
+#ifdef CONFIG_ROOT_NFS
+extern int nfs_root_init(char *nfsname);
+extern char nfs_root_name [];
+#endif
+
 /* This may be used only once, enforced by 'static int callable' */
 asmlinkage int sys_setup(void)
 {
@@ -100,6 +106,14 @@
 		{hpfs_read_super, "hpfs", 1, NULL});
 #endif
 
+#ifdef CONFIG_ROOT_NFS
+	if (nfs_root_name [0]){
+		if (nfs_root_init(nfs_root_name) < 0) {
+			printk(KERN_ERR "Root-NFS: Unable to mount NFS filesystem as /, using /dev/fd0 instead\n");
+			ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+		}
+	}
+#endif
 	mount_root();
 	return 0;
 }
diff -u --recursive --new-file v1.3.41/linux/fs/inode.c linux/fs/inode.c
--- v1.3.41/linux/fs/inode.c	Mon Sep 18 14:54:06 1995
+++ linux/fs/inode.c	Tue Nov 14 08:41:56 1995
@@ -31,7 +31,7 @@
 	return hash_table + hashfn(dev, i);
 }
 
-static void insert_inode_free(struct inode *inode)
+static inline void insert_inode_free(struct inode *inode)
 {
 	inode->i_next = first_inode;
 	inode->i_prev = first_inode->i_prev;
@@ -40,7 +40,7 @@
 	first_inode = inode;
 }
 
-static void remove_inode_free(struct inode *inode)
+static inline void remove_inode_free(struct inode *inode)
 {
 	if (first_inode == inode)
 		first_inode = first_inode->i_next;
@@ -63,7 +63,7 @@
 	h->inode = inode;
 }
 
-static void remove_inode_hash(struct inode *inode)
+static inline void remove_inode_hash(struct inode *inode)
 {
 	struct inode_hash_entry *h;
 	h = hash(inode->i_dev, inode->i_ino);
@@ -77,7 +77,7 @@
 	inode->i_hash_prev = inode->i_hash_next = NULL;
 }
 
-static void put_last_free(struct inode *inode)
+static inline void put_last_free(struct inode *inode)
 {
 	remove_inode_free(inode);
 	inode->i_prev = first_inode->i_prev;
@@ -225,7 +225,7 @@
 	unlock_inode(inode);
 }
 
-static void read_inode(struct inode * inode)
+static inline void read_inode(struct inode * inode)
 {
 	lock_inode(inode);
 	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode)
diff -u --recursive --new-file v1.3.41/linux/fs/nfs/Makefile linux/fs/nfs/Makefile
--- v1.3.41/linux/fs/nfs/Makefile	Fri Oct  6 14:35:15 1995
+++ linux/fs/nfs/Makefile	Thu Nov 16 08:37:53 1995
@@ -9,6 +9,11 @@
 
 O_TARGET := nfs.o
 O_OBJS   := proc.o sock.o rpcsock.o inode.o file.o dir.o symlink.o mmap.o
+
+ifdef CONFIG_ROOT_NFS
+O_OBJS += nfsroot.o
+endif
+
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.41/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.41/linux/fs/nfs/nfsroot.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/nfs/nfsroot.c	Thu Nov 16 08:54:24 1995
@@ -0,0 +1,849 @@
+/*
+ *  linux/fs/nfs/nfsroot.c
+ *
+ *  Copyright (C) 1995  Gero Kuhlmann <gero@gkminix.han.de>
+ *
+ *  Allow an NFS filesystem to be mounted as root. The way this works
+ *  is to first determine the local IP address via RARP. Then handle
+ *  the RPC negotiation with the system which replied to the RARP. The
+ *  actual mounting is done later, when init() is running.
+ */
+
+
+/* Define this to allow debugging output */
+#define NFSROOT_DEBUG 1
+
+/* Define the timeout for waiting for a RARP reply */
+#define RARP_TIMEOUT	30	/* 30 seconds */
+#define RARP_RETRIES	 5	/* 5 retries */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/param.h>
+#include <linux/utsname.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#ifdef CONFIG_AX25
+#include <net/ax25.h>	/* For AX25_P_IP */
+#endif
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <net/route.h>
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+/* #include <netinet/in.h> */
+
+#define IPPORT_RESERVED 1024
+
+/* Range of privileged ports */
+#define STARTPORT 600
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS	(ENDPORT - STARTPORT + 1)
+
+
+
+struct open_dev
+{
+	struct device *dev;
+	unsigned short old_flags;
+	struct open_dev *next;
+};
+
+static struct open_dev *open_base = NULL;
+static struct device *root_dev = NULL;
+static struct sockaddr_in myaddr;	/* My IP address */
+static struct sockaddr_in server;	/* Server IP address */
+static struct nfs_mount_data nfs_data;	/* NFS mount info */
+static char nfs_path[NFS_MAXPATHLEN];	/* Name of directory to mount */
+static int nfs_port;			/* Port to connect to for NFS service */
+
+
+
+/***************************************************************************
+
+			RARP Subroutines
+
+ ***************************************************************************/
+
+extern void arp_send(int type, int ptype, unsigned long target_ip, 
+			struct device *dev, unsigned long src_ip, 
+			unsigned char *dest_hw, unsigned char *src_hw,
+			unsigned char *target_hw);
+
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
+			struct packet_type *pt);
+
+
+static struct packet_type rarp_packet_type =
+{
+	0,  	/* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
+	NULL,	/* Listen to all devices */
+	root_rarp_recv,
+	NULL,
+	NULL
+};
+
+
+/*
+ *  For receiving rarp packets a packet type has to be registered. Also
+ *  initialize all devices for usage by RARP.
+ */
+static int root_rarp_open(void)
+{
+  struct open_dev *openp;
+  struct device *dev;
+  unsigned short old_flags;
+  int num;
+
+  /* Register the packet type */
+  rarp_packet_type.type=htons(ETH_P_RARP);
+  dev_add_pack(&rarp_packet_type);
+
+  /* Open all devices which allow RARP */
+  for (dev = dev_base, num = 0; dev != NULL; dev = dev->next) {
+	if (dev->type < ARPHRD_SLIP &&
+	    dev->family == AF_INET &&
+	    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOARP))) {
+		/* First up the interface */
+		old_flags = dev->flags;
+		dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+		if (!(old_flags & IFF_UP) && dev_open(dev)) {
+			dev->flags = old_flags;
+			continue;
+		}
+		openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
+						GFP_ATOMIC);
+		if (openp == NULL)
+			continue;
+		openp->dev = dev;
+		openp->old_flags = old_flags;
+		openp->next = open_base;
+		open_base = openp;
+		num++;
+	}
+  }
+  return num;
+}
+
+
+/*
+ *  Remove the packet type again when all rarp packets have been received
+ *  and restore the state of the device. However, keep the root device
+ *  open for the upcoming mount.
+ */
+static void root_rarp_close(void)
+{
+  struct open_dev *openp;
+  struct open_dev *nextp;
+
+  /* Deregister the packet type */
+  rarp_packet_type.type=htons(ETH_P_RARP);
+  dev_remove_pack(&rarp_packet_type);
+
+  /* Deactivate all previously opened devices except that one which is
+   * able to connect to a suitable server
+   */
+  openp = open_base;
+  while (openp != NULL) {
+	nextp = openp->next;
+	openp->next = NULL;
+	if (openp->dev != root_dev) {
+		if (!(openp->old_flags & IFF_UP))
+			dev_close(openp->dev);
+		openp->dev->flags = openp->old_flags;
+	}
+	kfree_s(openp, sizeof(struct open_dev));
+	openp = nextp;
+  }
+}
+
+
+/*
+ * Receive RARP packets.
+ */
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+  struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+  unsigned char *rarp_ptr = (unsigned char *)(rarp+1);
+  unsigned long sip, tip;
+  unsigned char *sha, *tha;		/* s for "source", t for "target" */
+  
+  /* If this test doesn't pass, its not IP, or we should ignore it anyway */
+  if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
+	kfree_skb(skb, FREE_READ);
+	return 0;
+  }
+
+  /* If it's not a RARP reply, delete it. */
+  if (rarp->ar_op != htons(ARPOP_RREPLY)) {
+	kfree_skb(skb, FREE_READ);
+	return 0;
+  }
+
+  /* If it's not ethernet or AX25, delete it. */
+  if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) || 
+#ifdef CONFIG_AX25
+      (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+       rarp->ar_pln != 4) {
+	kfree_skb(skb, FREE_READ);
+	return 0;
+  }
+  
+  /* Extract variable width fields */
+  sha = rarp_ptr;
+  rarp_ptr += dev->addr_len;
+  memcpy(&sip, rarp_ptr, 4);
+  rarp_ptr += 4;
+  tha = rarp_ptr;
+  rarp_ptr += dev->addr_len;
+  memcpy(&tip, rarp_ptr, 4);
+
+  /* Discard packets which are not meant for us. */
+  if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
+	kfree_skb(skb, FREE_READ);
+	return 0;
+  }
+
+  /* The packet is what we were looking for. Setup the global variables. */
+  cli();
+  if (root_dev != NULL) {
+	sti();
+	kfree_skb(skb, FREE_READ);
+	return 0;
+  }
+  root_dev = dev;
+  sti();
+
+  myaddr.sin_family = dev->family;
+  myaddr.sin_addr.s_addr = tip;
+  server.sin_family = dev->family;
+  if (!server.sin_addr.s_addr)
+    server.sin_addr.s_addr = sip;
+
+  kfree_skb(skb, FREE_READ);
+  return 0;
+}
+
+
+/*
+ * Send RARP request packet over all devices which allow RARP.
+ */
+static void root_rarp_send(void)
+{
+  struct open_dev *openp;
+  struct device *dev;
+
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Sending RARP request...\n");
+#endif
+
+  for (openp = open_base; openp != NULL; openp = openp->next) {
+	dev = openp->dev;
+	arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+						dev->dev_addr, dev->dev_addr);
+  }
+}
+
+
+/*
+ * Determine client and server IP numbers and appropriate device by using
+ * the RARP protocol.
+ */
+static int do_rarp(void)
+{
+  int retries = 0;
+  unsigned long timeout;
+
+  /* Open all devices and setup RARP protocol */
+  if (!root_rarp_open()) {
+	printk(KERN_ERR "NFS: No network device found to send RARP request to\n");
+	return -1;
+  }
+
+  /* Send RARP request and wait, until we get an answer. This loop seems
+   * to be a terrible waste of cpu time, but actually there is no process
+   * running at all, so we don't need to use any scheduler functions.
+   */
+  for (retries = 0; retries < RARP_RETRIES && root_dev == NULL; retries++) {
+	root_rarp_send();
+	timeout = jiffies + (RARP_TIMEOUT * HZ);
+	while (jiffies < timeout && root_dev == NULL)
+		;;
+  }
+
+  if (root_dev == NULL) {
+	printk(KERN_ERR "NFS: Timed out while waiting for RARP answer\n");
+	return -1;
+  }
+
+  root_rarp_close();
+
+  printk(KERN_NOTICE "NFS: ");
+  printk("Got RARP answer from %s, ", in_ntoa(server.sin_addr.s_addr));
+  printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
+
+  return 0;
+}
+
+
+
+
+/***************************************************************************
+
+			Routines to setup NFS
+
+ ***************************************************************************/
+
+extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
+			unsigned long gw, struct device *dev,
+			unsigned short mss, unsigned long window);
+
+
+/* The following integer options are recognized */
+static struct nfs_int_opts
+{
+	char *name;
+	int  *val;
+} root_int_opts[] = {
+	{ "port",	&nfs_port },
+	{ "rsize",	&nfs_data.rsize },
+	{ "wsize",	&nfs_data.wsize },
+	{ "timeo",	&nfs_data.timeo },
+	{ "retrans",	&nfs_data.retrans },
+	{ "acregmin",	&nfs_data.acregmin },
+	{ "acregmax",	&nfs_data.acregmax },
+	{ "acdirmin",	&nfs_data.acdirmin },
+	{ "acdirmax",	&nfs_data.acdirmax },
+	{ NULL,		NULL }};
+
+
+/* And now the flag options */
+static struct nfs_bool_opts
+{
+	char *name;
+	int  and_mask;
+	int  or_mask;
+} root_bool_opts[] = {
+	{ "soft",	~NFS_MOUNT_SOFT,	NFS_MOUNT_SOFT },
+	{ "hard",	~NFS_MOUNT_SOFT,	0 },
+	{ "intr",	~NFS_MOUNT_INTR,	NFS_MOUNT_INTR },
+	{ "nointr",	~NFS_MOUNT_INTR,	0 },
+	{ "posix",	~NFS_MOUNT_POSIX,	NFS_MOUNT_POSIX },
+	{ "noposix",	~NFS_MOUNT_POSIX,	0 },
+	{ "cto",	~NFS_MOUNT_NOCTO,	0 },
+	{ "nocto",	~NFS_MOUNT_NOCTO,	NFS_MOUNT_NOCTO },
+	{ "ac",		~NFS_MOUNT_NOAC,	0 },
+	{ "noac",	~NFS_MOUNT_NOAC,	NFS_MOUNT_NOAC },
+	{ NULL,		0,			0 }};
+
+
+unsigned long get_address (char **str)
+{
+   unsigned long l;
+   unsigned int val;
+   int i;
+   
+   l = 0;
+   for (i = 0; i < 4; i++) 
+     {
+	
+	l <<= 8;
+	if (**str != '\0')
+	  {
+	     
+	     val = 0;
+	     while (**str != '\0' && **str != '.' && **str != ':')
+	       {
+		  
+		  val *= 10;
+		  val += **str - '0';
+		  (*str)++;
+	       }
+	     
+	     l |= val;
+	     if (**str != '\0') 
+	       (*str)++;
+	  }
+	
+     }
+   
+   return(htonl(l));
+}
+
+/*
+ * Prepare the NFS data structure and parse any options
+ */
+static int root_nfs_parse(char *name)
+{
+  char buf[NFS_MAXPATHLEN];
+  char *cp, *options, *val;
+
+  /* get the host ip number */
+  if (*name >= '0' && *name <= '9'){
+    server.sin_addr.s_addr = get_address (&name);
+  }
+  /* Setup the server hostname */
+  cp = in_ntoa(server.sin_addr.s_addr);
+  strncpy(nfs_data.hostname, cp, 255);
+  nfs_data.addr = server;
+
+  /* Set the name of the directory to mount */
+  cp = in_ntoa(myaddr.sin_addr.s_addr);
+  strncpy(buf, name, 255);
+  if ((options = strchr(buf, ',')))
+	*options++ = '\0';
+  if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+	printk(KERN_ERR "NFS: Pathname for remote directory too long\n");
+	return -1;
+  }
+  sprintf(nfs_path, buf, cp);
+
+  /* Set some default values */
+  nfs_port          = -1;
+  nfs_data.version  = NFS_MOUNT_VERSION;
+  nfs_data.flags    = 0;
+  nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
+  nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
+  nfs_data.timeo    = 7;
+  nfs_data.retrans  = 3;
+  nfs_data.acregmin = 3;
+  nfs_data.acregmax = 60;
+  nfs_data.acdirmin = 30;
+  nfs_data.acdirmax = 60;
+
+  /* Process any options */
+  if (options) {
+	cp = strtok(options, ",");
+	while (cp) {
+		if ((val = strchr(cp, '='))) {
+			struct nfs_int_opts *opts = root_int_opts;
+			*val++ = '\0';
+			while (opts->name && strcmp(opts->name, cp))
+				opts++;
+			if (opts->name)
+				*(opts->val) = (int) simple_strtoul(val, NULL, 10);
+		} else {
+			struct nfs_bool_opts *opts = root_bool_opts;
+			while (opts->name && strcmp(opts->name, cp))
+				opts++;
+			if (opts->name) {
+				nfs_data.flags &= opts->and_mask;
+				nfs_data.flags |= opts->or_mask;
+			}
+		}
+		cp = strtok(NULL, ",");
+	}
+  }
+
+  return 0;
+}
+
+
+/*
+ * Tell the user what's going on.
+ */
+static void root_nfs_print(void)
+{
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Mounting %s on server %s as root\n",
+		nfs_path, nfs_data.hostname);
+  printk(KERN_NOTICE "NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+		nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
+  printk(KERN_NOTICE "NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+		nfs_data.acregmin, nfs_data.acregmax,
+		nfs_data.acdirmin, nfs_data.acdirmax);
+  printk(KERN_NOTICE "NFS:     port = %d, flags = %08x\n",
+		nfs_port, nfs_data.flags);
+#endif
+}
+
+
+/*
+ * Set the interface address and configure a route to the server.
+ */
+static void root_nfs_setup(void)
+{
+  struct rtentry server_route;
+  struct sockaddr_in *sin;
+
+  /* Setup the device correctly */
+  root_dev->family     = myaddr.sin_family;
+  root_dev->pa_addr    = myaddr.sin_addr.s_addr;
+  root_dev->pa_mask    = ip_get_mask(myaddr.sin_addr.s_addr);
+  root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
+  root_dev->pa_dstaddr = 0;
+
+  sin=(struct sockaddr_in *)&server_route.rt_dst;
+  *sin=server;
+  sin=(struct sockaddr_in *)&server_route.rt_genmask;
+  sin->sin_family=AF_INET;
+  sin->sin_addr.s_addr= root_dev->pa_mask;
+  server_route.rt_dev[0]=0;
+  server_route.rt_flags=RTF_HOST|RTF_UP;
+  
+  /* Now add a route to the server */
+  if(ip_rt_new(&server_route)==-1)
+  	printk("Unable to add NFS server route.\n");
+}
+
+
+/*
+ * Get the necessary IP addresses and prepare for mounting the required
+ * NFS filesystem.
+ */
+int nfs_root_init(char *nfsname)
+{
+  /* Initialize network device and get local and server IP address */
+  if (do_rarp() < 0)
+	return -1;
+
+  /* Initialize the global variables necessary for NFS. The server
+   * directory is actually mounted after init() has been started.
+   */
+  if (root_nfs_parse(nfsname) < 0)
+	return -1;
+  root_nfs_print();
+  root_nfs_setup();
+  return 0;
+}
+
+
+
+
+/***************************************************************************
+
+		Routines to actually mount the root directory
+
+ ***************************************************************************/
+
+static struct file nfs_file;		/* File descriptor containing socket */
+static struct inode nfs_inode;		/* Inode containing socket */
+static int *rpc_packet = NULL;		/* RPC packet */
+
+extern asmlinkage int sys_socketcall(int call, unsigned long *args);
+extern struct socket *socki_lookup(struct inode *inode);
+
+
+/*
+ * Open a UDP socket.
+ */
+static int root_nfs_open(void)
+{
+  struct file *filp;
+  unsigned long opt[] = { AF_INET, SOCK_DGRAM, IPPROTO_UDP };
+
+  /* Open the socket */
+  if ((nfs_data.fd = sys_socketcall(SYS_SOCKET, opt)) < 0) {
+	printk(KERN_ERR "NFS: Cannot open UDP socket\n");
+	return -1;
+  }
+
+  /* Copy the file and inode data area so that we can remove the
+   * file lateron without killing the socket. After all this the
+   * closing routine just needs to remove the file pointer from
+   * the init-task descriptor.
+   */
+  filp = current->files->fd[nfs_data.fd];
+  memcpy(&nfs_file, filp, sizeof(struct file));
+  nfs_file.f_next = nfs_file.f_prev = NULL;
+  current->files->fd[nfs_data.fd] = &nfs_file;
+  filp->f_count = 0;		/* Free the file descriptor */
+
+  memcpy(&nfs_inode, nfs_file.f_inode, sizeof(struct inode));
+  nfs_inode.i_hash_next = nfs_inode.i_hash_prev = NULL;
+  nfs_inode.i_next = nfs_inode.i_prev = NULL;
+  clear_inode(nfs_file.f_inode);
+  nfs_file.f_inode = &nfs_inode;
+  nfs_inode.u.socket_i.inode = &nfs_inode;
+  nfs_file.private_data = NULL;
+
+  return 0;
+}
+
+
+/*
+ * Close the UDP file descriptor. The main part of preserving the socket
+ * has already been done after opening it. Now we have to remove the
+ * file descriptor from the init task.
+ */
+static void root_nfs_close(int close_all)
+{
+  /* Remove the file from the list of open files */
+  current->files->fd[nfs_data.fd] = NULL;
+  if (current->files->count > 0)
+	current->files->count--;
+
+  /* Clear memory use by the RPC packet */
+  if (rpc_packet != NULL)
+	kfree_s(rpc_packet, nfs_data.wsize + 1024);
+
+  /* In case of an error we also have to close the socket again (sigh) */
+  if (close_all) {
+	nfs_inode.u.socket_i.inode = NULL;	/* The inode is already cleared */
+	if (nfs_file.f_op->release)
+		nfs_file.f_op->release(&nfs_inode, &nfs_file);
+  }
+}
+
+
+/*
+ * Find a suitable listening port and bind to it
+ */
+static int root_nfs_bind(void)
+{
+  int res = -1;
+  short port = STARTPORT;
+  struct sockaddr_in *sin = &myaddr;
+  int i;
+
+  if (nfs_inode.u.socket_i.ops->bind) {
+	for (i = 0; i < NPORTS && res < 0; i++) {
+		sin->sin_port = htons(port++);
+		if (port > ENDPORT) {
+			port = STARTPORT;
+		}
+		res = nfs_inode.u.socket_i.ops->bind(&nfs_inode.u.socket_i,
+			(struct sockaddr *) sin, sizeof(struct sockaddr_in));
+	}
+  }
+  if (res < 0) {
+	printk(KERN_ERR "NFS: Cannot find a suitable listening port\n");
+	root_nfs_close(1);
+	return -1;
+  }
+
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Binding to listening port %d\n", port);
+#endif
+  return 0;
+}
+
+
+/*
+ * Send an RPC request and wait for the answer
+ */
+static int *root_nfs_call(int *end)
+{
+  struct file *filp;
+  struct socket *sock;
+  int dummylen;
+  static struct nfs_server s =
+	{ &nfs_file, /* struct file * */
+	  0,         /* struct rsock * */
+	  {
+	     0, "",
+	  },         /* toaddr */
+          0,         /* lock */
+	  NULL,      /* wait queue */
+	  NFS_MOUNT_SOFT, /* flags */
+	  0, 0,           /* rsize, wsize */
+	  0,              /* timeo */
+	  0,              /* retrans */
+	  3*HZ, 60*HZ, 30*HZ, 60*HZ, "\0" };
+
+  filp = &nfs_file;
+  sock = &((filp->f_inode)->u.socket_i);
+    
+  /* extract the other end of the socket into server->toaddr */
+  sock->ops->getname(sock, &(s.toaddr), &dummylen, 1) ;
+  ((struct sockaddr_in *) &s.toaddr)->sin_port   = server.sin_port;
+  ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
+  ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
+  
+  s.rsock = rpc_makesock(filp);
+  s.flags = nfs_data.flags;
+  s.rsize = nfs_data.rsize;
+  s.wsize = nfs_data.wsize;
+  s.timeo = nfs_data.timeo * HZ / 10;
+  s.retrans = nfs_data.retrans;
+  strcpy(s.hostname, nfs_data.hostname);
+
+  /* First connect the UDP socket to a server port, then send the packet
+   * out, and finally check wether the answer is OK.
+   */
+  if (nfs_inode.u.socket_i.ops->connect &&
+      nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+		(struct sockaddr *) &server, sizeof(struct sockaddr_in),
+		nfs_file.f_flags) < 0)
+	return NULL;
+  if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
+	return NULL;
+  return rpc_verify(rpc_packet);
+}
+
+
+/*
+ * Create an RPC packet header
+ */
+static int *root_nfs_header(int proc, int program, int version)
+{
+  int groups[] = { 0, NOGROUP };
+
+  if (rpc_packet == NULL) {
+	if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
+		printk(KERN_ERR "NFS: Cannot allocate UDP buffer\n");
+		return NULL;
+	}
+  }
+  strcpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr));
+  return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
+}
+
+
+/*
+ * Query server portmapper for the port of a daemon program
+ */
+static int root_nfs_get_port(int program, int version)
+{
+  int *p;
+
+  /* Prepare header for portmap request */
+  server.sin_port = htons(NFS_PMAP_PORT);
+  p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
+  if (!p)
+	return -1;
+
+  /* Set arguments for portmapper */
+  *p++ = htonl(program);
+  *p++ = htonl(version);
+  *p++ = htonl(IPPROTO_UDP);
+  *p++ = 0;
+
+  /* Send request to server portmapper */
+  if ((p = root_nfs_call(p)) == NULL)
+	return -1;
+
+  return ntohl(*p);
+}
+
+
+/*
+ * Get portnumbers for mountd and nfsd from server
+ */
+static int root_nfs_ports(void)
+{
+  int port;
+
+  if (nfs_port < 0) {
+	if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
+		printk(KERN_ERR "NFS: Unable to get nfsd port number from server, using default\n");
+		port = NFS_NFS_PORT;
+	}
+	nfs_port = port;
+#ifdef NFSROOT_DEBUG
+	printk(KERN_NOTICE "NFS: Portmapper on server returned %d as nfsd port\n", port);
+#endif
+  }
+
+  if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
+	printk(KERN_ERR "NFS: Unable to get mountd port number from server, using default\n");
+	port = NFS_MOUNT_PORT;
+  }
+  server.sin_port = htons(port);
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Portmapper on server returned %d as mountd port\n", port);
+#endif
+
+  return 0;
+}
+
+
+/*
+ * Get a file handle from the server for the directory which is to be mounted
+ */
+static int root_nfs_get_handle(void)
+{
+  int len, status, *p;
+
+  /* Prepare header for mountd request */
+  p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
+  if (!p) {
+	root_nfs_close(1);
+	return -1;
+  }
+
+  /* Set arguments for mountd */
+  len = strlen(nfs_path);
+  *p++ = htonl(len);
+  memcpy(p, nfs_path, len);
+  len = (len + 3) >> 2;
+  p[len] = 0;
+  p += len;
+
+  /* Send request to server portmapper */
+  if ((p = root_nfs_call(p)) == NULL) {
+	root_nfs_close(1);
+	return -1;
+  }
+
+  status = ntohl(*p++);
+  if (status == 0) {
+	nfs_data.root = *((struct nfs_fh *) p);
+  } else {
+	printk(KERN_ERR "NFS: Server returned error %d while mounting %s\n",
+							status, nfs_path);
+	root_nfs_close(1);
+	return -1;
+  }
+
+  return 0;
+}
+
+
+/*
+ * Now actually mount the given directory
+ */
+static int root_nfs_do_mount(struct super_block *sb)
+{
+  /* First connect to the nfsd port on the server */
+  server.sin_port = htons(nfs_port);
+  nfs_data.addr = server;
+  if (nfs_inode.u.socket_i.ops->connect &&
+      nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+		(struct sockaddr *) &server, sizeof(struct sockaddr_in),
+		nfs_file.f_flags) < 0) {
+	root_nfs_close(1);
+	return -1;
+  }
+
+  /* Now (finally ;-)) read the super block for mounting */
+  if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
+	root_nfs_close(1);
+	return -1;
+  }
+
+  return 0;
+}
+
+
+/*
+ * Get the NFS port numbers and file handle, and then read the super-
+ * block for mounting.
+ */
+int nfs_root_mount(struct super_block *sb)
+{
+  if (root_nfs_open() < 0)
+	return -1;
+  if (root_nfs_bind() < 0)
+	return -1;
+  if (root_nfs_ports() < 0)
+	return -1;
+  if (root_nfs_get_handle() < 0)
+	return -1;
+  if (root_nfs_do_mount(sb) < 0)
+	return -1;
+  root_nfs_close(0);
+  return 0;
+}
diff -u --recursive --new-file v1.3.41/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v1.3.41/linux/fs/nfs/proc.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/nfs/proc.c	Tue Nov 14 16:03:52 1995
@@ -818,7 +818,8 @@
  * Here are a few RPC-assist functions.
  */
 
-static int *nfs_rpc_header(int *p, int procedure, int ruid)
+int *rpc_header(int *p, int procedure, int program, int version,
+					int uid, int gid, int *groups)
 {
 	int *p1, *p2;
 	int i;
@@ -832,18 +833,18 @@
 	*p++ = htonl(++xid);
 	*p++ = htonl(RPC_CALL);
 	*p++ = htonl(RPC_VERSION);
-	*p++ = htonl(NFS_PROGRAM);
-	*p++ = htonl(NFS_VERSION);
+	*p++ = htonl(program);
+	*p++ = htonl(version);
 	*p++ = htonl(procedure);
 	*p++ = htonl(RPC_AUTH_UNIX);
 	p1 = p++;
 	*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
 	p = xdr_encode_string(p, (char *) sys);
-	*p++ = htonl(ruid ? current->uid : current->fsuid);
-	*p++ = htonl(current->egid);
+	*p++ = htonl(uid);
+	*p++ = htonl(gid);
 	p2 = p++;
-	for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++)
-		*p++ = htonl(current->groups[i]);
+	for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++)
+		*p++ = htonl(groups[i]);
 	*p2 = htonl(i);
 	*p1 = htonl((p - (p1 + 1)) << 2);
 	*p++ = htonl(RPC_AUTH_NULL);
@@ -851,7 +852,16 @@
 	return p;
 }
 
-static int *nfs_rpc_verify(int *p)
+
+static int *nfs_rpc_header(int *p, int procedure, int ruid)
+{
+	return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
+			(ruid ? current->uid : current->fsuid),
+			current->egid, current->groups);
+}
+
+
+int *rpc_verify(int *p)
 {
 	unsigned int n;
 
@@ -882,7 +892,14 @@
 	}
 	return p;
 }
-	
+
+
+static int *nfs_rpc_verify(int *p)
+{
+	return rpc_verify(p);
+}
+
+
 /*
  * We need to translate between nfs status return values and
  * the local errno values which may not be the same.
diff -u --recursive --new-file v1.3.41/linux/fs/super.c linux/fs/super.c
--- v1.3.41/linux/fs/super.c	Mon Sep 18 14:54:08 1995
+++ linux/fs/super.c	Tue Nov 14 16:03:52 1995
@@ -6,6 +6,8 @@
 
 /*
  * super.c contains code to handle the super-block tables.
+ *
+ * GK 2/5/95  -  Changed to support mounting the root fs via NFS
  */
 #include <stdarg.h>
 
@@ -34,6 +36,10 @@
 
 static int do_remount_sb(struct super_block *sb, int flags, char * data);
 
+#ifdef CONFIG_ROOT_NFS
+extern int nfs_root_mount(struct super_block *sb);
+#endif
+
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
 
@@ -677,6 +683,35 @@
 	int retval;
 
 	memset(super_blocks, 0, sizeof(super_blocks));
+#ifdef CONFIG_ROOT_NFS
+	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
+		ROOT_DEV = 0;
+		if ((fs_type = get_fs_type("nfs"))) {
+			sb = &super_blocks[0];
+			sb->s_dev = get_unnamed_dev();
+			sb->s_flags = root_mountflags & ~MS_RDONLY;
+			if (nfs_root_mount(sb) >= 0) {
+				inode = sb->s_mounted;
+				inode->i_count += 3 ;
+				sb->s_covered = inode;
+				sb->s_rd_only = 0;
+				sb->s_dirt = 0;
+				sb->s_type = fs_type;
+				current->fs->pwd = inode;
+				current->fs->root = inode;
+				ROOT_DEV = sb->s_dev;
+				printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+				return;
+			}
+			sb->s_dev = 0;
+		}
+		if (!ROOT_DEV) {
+			printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
+			ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+		}
+	}
+#endif
+
 #ifdef CONFIG_BLK_DEV_FD
 	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
 		printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
diff -u --recursive --new-file v1.3.41/linux/include/asm-i386/ioctl.h linux/include/asm-i386/ioctl.h
--- v1.3.41/linux/include/asm-i386/ioctl.h	Mon Nov 13 12:36:46 1995
+++ linux/include/asm-i386/ioctl.h	Wed Nov 15 09:15:02 1995
@@ -43,8 +43,8 @@
  * Direction bits.
  */
 #define _IOC_NONE	0U
-#define _IOC_READ	1U
-#define _IOC_WRITE	2U
+#define _IOC_WRITE	1U
+#define _IOC_READ	2U
 
 #define _IOC(dir,type,nr,size) \
 	(((dir)  << _IOC_DIRSHIFT) | \
diff -u --recursive --new-file v1.3.41/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.41/linux/include/asm-i386/smp.h	Wed Nov  8 07:11:41 1995
+++ linux/include/asm-i386/smp.h	Tue Nov 14 16:03:56 1995
@@ -239,7 +239,7 @@
  *	processes are run.
  */
  
-#define PROC_CHANGE_PENALTY	5		/* Schedule penalty */
+#define PROC_CHANGE_PENALTY	20		/* Schedule penalty */
 
 #endif
 #endif
diff -u --recursive --new-file v1.3.41/linux/include/linux/bios32.h linux/include/linux/bios32.h
--- v1.3.41/linux/include/linux/bios32.h	Wed Aug  2 13:21:16 1995
+++ linux/include/linux/bios32.h	Tue Nov 14 07:35:31 1995
@@ -24,7 +24,7 @@
 
 /*
  * Error values that may be returned by the PCI bios.  Use
- * pci_strbioserr() to convert to a printable string.
+ * pcibios_strerror() to convert to a printable string.
  */
 #define PCIBIOS_SUCCESSFUL		0x00
 #define PCIBIOS_FUNC_NOT_SUPPORTED	0x81
diff -u --recursive --new-file v1.3.41/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v1.3.41/linux/include/linux/cyclades.h	Mon Oct 23 18:02:18 1995
+++ linux/include/linux/cyclades.h	Tue Nov 14 07:43:33 1995
@@ -5,8 +5,17 @@
  * Randolph Bentson <bentson@grieg.seaslug.org>.
  *
  * This file contains the general definitions for the cyclades.c driver
+ *$Log: cyclades.h,v $
+ * Revision 1.5  1995/11/13  21:13:31  bentson
+ * changes suggested by Michael Chastain <mec@duracef.shout.net>
+ * to support use of this file in non-kernel applications
+ *
+ *
  */
 
+#ifndef _LINUX_CYCLADES_H
+#define _LINUX_CYCLADES_H
+
 /* PCI vendor and device ID's */
 
 #ifndef PCI_VENDOR_ID_CYCLADES
@@ -17,6 +26,27 @@
 #define	PCI_DEVICE_ID_CYCLOMY	0x0100
 #endif
 
+struct cyclades_monitor {
+        unsigned long           int_count;
+        unsigned long           char_count;
+        unsigned long           char_max;
+        unsigned long           char_last;
+};
+
+#define CYCLADES_MAGIC  0x4359
+
+#define CYGETMON                0x435901
+#define CYGETTHRESH             0x435902
+#define CYSETTHRESH             0x435903
+#define CYGETDEFTHRESH          0x435904
+#define CYSETDEFTHRESH          0x435905
+#define CYGETTIMEOUT            0x435906
+#define CYSETTIMEOUT            0x435907
+#define CYGETDEFTIMEOUT         0x435908
+#define CYSETDEFTIMEOUT         0x435909
+
+#ifdef __KERNEL__
+
 /* Per card data structure */
 
 struct cyclades_card {
@@ -31,13 +61,6 @@
   int filler;
 };
 
-struct cyclades_monitor {
-        unsigned long           int_count;
-        unsigned long           char_count;
-        unsigned long           char_max;
-        unsigned long           char_last;
-};
-
 /*
  * This is our internal structure for each serial port's state.
  * 
@@ -47,9 +70,6 @@
  * For definitions of the flags field, see tty.h
  */
 
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
 struct cyclades_port {
 	int                     magic;
 	int                     type;
@@ -87,18 +107,6 @@
         struct cyclades_monitor mon;
 };
 
-#define CYCLADES_MAGIC  0x4359
-
-#define CYGETMON                0x435901
-#define CYGETTHRESH             0x435902
-#define CYSETTHRESH             0x435903
-#define CYGETDEFTHRESH          0x435904
-#define CYSETDEFTHRESH          0x435905
-#define CYGETTIMEOUT            0x435906
-#define CYSETTIMEOUT            0x435907
-#define CYGETDEFTIMEOUT         0x435908
-#define CYSETDEFTIMEOUT         0x435909
-
 /*
  * Events are used to schedule things to happen at timer-interrupt
  * time, instead of at cy interrupt time.
@@ -281,3 +289,6 @@
 #define CyMAX_CHAR_FIFO	12
 
 /***************************************************************************/
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CYCLADES_H */
diff -u --recursive --new-file v1.3.41/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v1.3.41/linux/include/linux/etherdevice.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/etherdevice.h	Tue Nov 14 16:03:56 1995
@@ -34,7 +34,9 @@
 extern int		eth_rebuild_header(void *buff, struct device *dev,
 			unsigned long dst, struct sk_buff *skb);
 extern unsigned short	eth_type_trans(struct sk_buff *skb, struct device *dev);
-extern void 		eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr);
+extern void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
+				  unsigned short htype, __u32 daddr);
+extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
 extern void		eth_copy_and_sum(struct sk_buff *dest,
 				unsigned char *src, int length, int base);
 extern struct device	* init_etherdev(struct device *, int);
diff -u --recursive --new-file v1.3.41/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.41/linux/include/linux/fs.h	Mon Oct 23 18:02:18 1995
+++ linux/include/linux/fs.h	Tue Nov 14 16:45:06 1995
@@ -496,8 +496,18 @@
 extern void ll_rw_page(int rw, kdev_t dev, unsigned long nr, char * buffer);
 extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer);
 extern int is_read_only(kdev_t dev);
-extern void brelse(struct buffer_head * buf);
-extern void bforget(struct buffer_head * buf);
+extern void __brelse(struct buffer_head *buf);
+extern inline void brelse(struct buffer_head *buf)
+{
+	if (buf)
+		__brelse(buf);
+}
+extern void __bforget(struct buffer_head *buf);
+extern inline void bforget(struct buffer_head *buf)
+{
+	if (buf)
+		__bforget(buf);
+}
 extern void set_blocksize(kdev_t dev, int size);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
 extern unsigned long bread_page(unsigned long addr,kdev_t dev,int b[],int size,int no_share);
diff -u --recursive --new-file v1.3.41/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.3.41/linux/include/linux/if_arp.h	Mon Aug 28 14:52:23 1995
+++ linux/include/linux/if_arp.h	Tue Nov 14 16:03:56 1995
@@ -41,6 +41,8 @@
 #define ARPHRD_PPP	512
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
 #define ARPHRD_TUNNEL6	769		/* IPIP6 tunnel			*/
+#define ARPHRD_FRAD	770		/* Frame Relay			*/
+#define ARPHRD_SKIP	771		/* SKIP vif			*/
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
@@ -51,6 +53,14 @@
 
 /* ARP ioctl request. */
 struct arpreq {
+  struct sockaddr	arp_pa;		/* protocol address		*/
+  struct sockaddr	arp_ha;		/* hardware address		*/
+  int			arp_flags;	/* flags			*/
+  struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
+  char			arp_dev[16];
+};
+
+struct arpreq_old {
   struct sockaddr	arp_pa;		/* protocol address		*/
   struct sockaddr	arp_ha;		/* hardware address		*/
   int			arp_flags;	/* flags			*/
diff -u --recursive --new-file v1.3.41/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v1.3.41/linux/include/linux/if_ppp.h	Mon Oct 23 18:02:18 1995
+++ linux/include/linux/if_ppp.h	Tue Nov 14 16:03:56 1995
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -50,6 +50,8 @@
 #define PPP_VERSION	"2.2.0"
 #define PPP_MAGIC	0x5002	/* Magic value for the ppp structure */
 #define PROTO_IPX	0x002b	/* protocol numbers */
+#define PROTO_DNA_RT    0x0027  /* DNA Routing */
+
 
 /*
  * Bit definitions for flags.
diff -u --recursive --new-file v1.3.41/linux/include/linux/ioport.h linux/include/linux/ioport.h
--- v1.3.41/linux/include/linux/ioport.h	Mon Jan  9 07:56:28 1995
+++ linux/include/linux/ioport.h	Tue Nov 14 07:48:52 1995
@@ -26,7 +26,7 @@
 
 #define HAVE_AUTOIRQ
 extern void *irq2dev_map[16];		/* Use only if you own the IRQ. */
-extern void autoirq_setup(int waittime);
+extern int autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 
 #endif	/* _LINUX_PORTIO_H */
diff -u --recursive --new-file v1.3.41/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.3.41/linux/include/linux/ip_fw.h	Mon Oct 23 18:02:19 1995
+++ linux/include/linux/ip_fw.h	Thu Nov 16 09:28:19 1995
@@ -59,6 +59,23 @@
 #define IP_FW_MAX_PORTS	10      		/* A reasonable maximum */
 	unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
 	unsigned long  fw_pcnt,fw_bcnt;		/* Packet and byte counters */
+	unsigned short fw_priority;		/* Revised packet priority */
+};
+
+struct ip_fw_old
+{
+	struct ip_fw  *fw_next;			/* Next firewall on chain */
+	struct in_addr fw_src, fw_dst;		/* Source and destination IP addr */
+	struct in_addr fw_smsk, fw_dmsk;	/* Mask for src and dest IP addr */
+	struct in_addr fw_via;			/* IP address of interface "via" */
+	unsigned short fw_flg;			/* Flags word */
+	unsigned short fw_nsp, fw_ndp;          /* N'of src ports and # of dst ports */
+						/* in ports array (dst ports follow */
+    						/* src ports; max of 10 ports in all; */
+    						/* count of 0 means match all ports) */
+#define IP_FW_MAX_PORTS	10      		/* A reasonable maximum */
+	unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
+	unsigned long  fw_pcnt,fw_bcnt;		/* Packet and byte counters */
 };
 
 /*
diff -u --recursive --new-file v1.3.41/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.41/linux/include/linux/major.h	Thu Nov  9 11:23:52 1995
+++ linux/include/linux/major.h	Tue Nov 14 16:03:57 1995
@@ -50,6 +50,7 @@
  * 32 -                        philips/lms cm206 cdrom
  * 33 -                        ide2
  * 34 - z8530 driver           ide3
+ * 36 - netlink
  */
 
 #define UNNAMED_MAJOR	0
@@ -97,6 +98,7 @@
 #define CM206_CDROM_MAJOR 32
 #define IDE2_MAJOR	33
 #define IDE3_MAJOR	34
+#define NETLINK_MAJOR	36
 
 /*
  * Tests for SCSI devices.
diff -u --recursive --new-file v1.3.41/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.41/linux/include/linux/mm.h	Thu Nov  9 11:23:52 1995
+++ linux/include/linux/mm.h	Tue Nov 14 16:45:06 1995
@@ -215,8 +215,6 @@
 /* mmap.c */
 extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long off);
-extern struct vm_area_struct * find_vma (struct task_struct *, unsigned long);
-extern struct vm_area_struct * find_vma_intersection (struct task_struct *, unsigned long, unsigned long);
 extern void merge_segments(struct task_struct *, unsigned long, unsigned long);
 extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
 extern void remove_shared_vm_struct(struct vm_area_struct *);
@@ -244,6 +242,40 @@
 
 #define GFP_LEVEL_MASK 0xf
 
+#define avl_empty	(struct vm_area_struct *) NULL
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
+{
+	struct vm_area_struct * result = NULL;
+	struct vm_area_struct * tree;
+
+	if (!task->mm)
+		return NULL;
+	for (tree = task->mm->mmap_avl ; ; ) {
+		if (tree == avl_empty)
+			return result;
+		if (tree->vm_end > addr) {
+			if (tree->vm_start <= addr)
+				return tree;
+			result = tree;
+			tree = tree->vm_avl_left;
+		} else
+			tree = tree->vm_avl_right;
+	}
+}
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+   NULL if none.  Assume start_addr < end_addr. */
+static inline struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+	struct vm_area_struct * vma;
+
+	vma = find_vma(task,start_addr);
+	if (!vma || end_addr <= vma->vm_start)
+		return NULL;
+	return vma;
+}
 
 /*
  * vm_ops not present page codes for shared memory.
diff -u --recursive --new-file v1.3.41/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.3.41/linux/include/linux/netdevice.h	Wed Nov  8 07:11:42 1995
+++ linux/include/linux/netdevice.h	Thu Nov 16 09:20:47 1995
@@ -24,12 +24,11 @@
 #ifndef _LINUX_NETDEVICE_H
 #define _LINUX_NETDEVICE_H
 
+#include <linux/config.h>
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/skbuff.h>
 
-#include <linux/config.h>
-
 /* for future expansion when we will have different priorities. */
 #define DEV_NUMBUFFS	3
 #define MAX_ADDR_LEN	7
@@ -61,6 +60,19 @@
 	unsigned short dmi_users;
 };
 
+struct hh_cache
+{
+	struct hh_cache *hh_next;
+	unsigned long	hh_refcnt;	/* number of users */
+	void		*hh_arp;	/* Opaque pointer, used by
+					 * any address resolution module,
+					 * not only ARP.
+					 */
+	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
+	char		hh_uptodate;	/* hh_data is valid */
+	char		hh_data[16];    /* cached hardware header */
+};
+
 /*
  * The DEVICE structure.
  * Actually, this whole structure is a big mistake.  It mixes I/O
@@ -162,12 +174,14 @@
   void			  (*set_multicast_list)(struct device *dev,
   					 int num_addrs, void *addrs);
 #define HAVE_SET_MAC_ADDR  		 
-  int			  (*set_mac_address)(struct device *dev, struct sockaddr *addr);
+  int			  (*set_mac_address)(struct device *dev, void *addr);
 #define HAVE_PRIVATE_IOCTL
   int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
 #define HAVE_SET_CONFIG
   int			  (*set_config)(struct device *dev, struct ifmap *map);
-  void			  (*header_cache)(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr);
+#define HAVE_HEADER_CACHE
+  void			  (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, unsigned short htype, __u32 daddr);
+  void			  (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
 };
 
 
@@ -201,6 +215,7 @@
 extern unsigned long	ip_my_addr(void);
 extern unsigned long	ip_get_mask(unsigned long addr);
 extern struct device 	*ip_dev_find(unsigned long addr);
+extern struct device    *dev_getbytype(unsigned short type);
 
 extern void		dev_add_pack(struct packet_type *pt);
 extern void		dev_remove_pack(struct packet_type *pt);
diff -u --recursive --new-file v1.3.41/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v1.3.41/linux/include/linux/nfs_fs.h	Sat Sep  9 15:26:53 1995
+++ linux/include/linux/nfs_fs.h	Tue Nov 14 16:03:57 1995
@@ -86,6 +86,9 @@
 			    int cookie, int count, struct nfs_entry *entry);
 extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 			    struct nfs_fsinfo *res);
+extern int *rpc_header(int *p, int procedure, int program, int version,
+				int uid, int gid, int *groups);
+extern int *rpc_verify(int *p);
 
 /* linux/fs/nfs/sock.c */
 
diff -u --recursive --new-file v1.3.41/linux/include/linux/nfs_mount.h linux/include/linux/nfs_mount.h
--- v1.3.41/linux/include/linux/nfs_mount.h	Wed Aug 10 19:26:43 1994
+++ linux/include/linux/nfs_mount.h	Tue Nov 14 16:03:57 1995
@@ -17,7 +17,19 @@
  * but here they are anyway.
  */
 
-#define NFS_MOUNT_VERSION	1	/* current version */
+#define NFS_NFS_PROGRAM		100003	/* nfsd program number */
+#define NFS_NFS_VERSION		2	/* nfsd version */
+#define NFS_NFS_PORT		2049	/* portnumber on server for nfsd */
+
+#define NFS_MOUNT_PROGRAM	100005	/* mountd program number */
+#define NFS_MOUNT_VERSION	1	/* mountd version */
+#define NFS_MOUNT_PROC		1	/* mount process id */
+#define NFS_MOUNT_PORT		627	/* portnumber on server for mountd */
+
+#define NFS_PMAP_PROGRAM	100000	/* portmap program number */
+#define NFS_PMAP_VERSION	2	/* portmap version */
+#define NFS_PMAP_PROC		3	/* portmap getport id */
+#define NFS_PMAP_PORT		111	/* portnumber on server for portmap */
 
 struct nfs_mount_data {
 	int version;			/* 1 */
diff -u --recursive --new-file v1.3.41/linux/include/linux/pci.h linux/include/linux/pci.h
--- v1.3.41/linux/include/linux/pci.h	Mon Nov 13 12:36:46 1995
+++ linux/include/linux/pci.h	Tue Nov 14 07:35:31 1995
@@ -541,7 +541,6 @@
 
 extern struct pci_dev_info *pci_lookup_dev (unsigned int vendor,
 					    unsigned int dev);
-extern const char *pci_strbioserr (int error);
 extern const char *pci_strclass (unsigned int class);
 extern const char *pci_strvendor (unsigned int vendor);
 extern const char *pci_strdev (unsigned int vendor, unsigned int device);
diff -u --recursive --new-file v1.3.41/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.41/linux/include/linux/proc_fs.h	Wed Nov  8 07:11:42 1995
+++ linux/include/linux/proc_fs.h	Tue Nov 14 16:45:12 1995
@@ -86,6 +86,7 @@
 	PROC_NET_NR_NEIGH,
 	PROC_NET_NR,
 	PROC_NET_SOCKSTAT,
+	PROC_NET_RTCACHE,
 	PROC_NET_LAST
 };
 
diff -u --recursive --new-file v1.3.41/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.3.41/linux/include/linux/sockios.h	Wed Nov  8 07:11:42 1995
+++ linux/include/linux/sockios.h	Tue Nov 14 16:03:57 1995
@@ -54,9 +54,12 @@
 /* end multicast support change */
 
 /* ARP cache control calls. */
-#define SIOCDARP	0x8950		/* delete ARP table entry	*/
-#define SIOCGARP	0x8951		/* get ARP table entry		*/
-#define SIOCSARP	0x8952		/* set ARP table entry		*/
+#define OLD_SIOCDARP	0x8950		/* old delete ARP table entry	*/
+#define OLD_SIOCGARP	0x8951		/* old get ARP table entry	*/
+#define OLD_SIOCSARP	0x8952		/* old set ARP table entry	*/
+#define SIOCDARP	0x8953		/* delete ARP table entry	*/
+#define SIOCGARP	0x8954		/* get ARP table entry		*/
+#define SIOCSARP	0x8955		/* set ARP table entry		*/
 
 /* RARP cache control calls. */
 #define SIOCDRARP	0x8960		/* delete RARP table entry	*/
diff -u --recursive --new-file v1.3.41/linux/include/linux/tcp.h linux/include/linux/tcp.h
--- v1.3.41/linux/include/linux/tcp.h	Tue Aug 15 20:39:05 1995
+++ linux/include/linux/tcp.h	Tue Nov 14 16:03:57 1995
@@ -17,10 +17,6 @@
 #ifndef _LINUX_TCP_H
 #define _LINUX_TCP_H
 
-
-#define HEADER_SIZE	128		/* maximum header size		*/
-
-
 struct tcphdr {
 	__u16	source;
 	__u16	dest;
diff -u --recursive --new-file v1.3.41/linux/include/net/arp.h linux/include/net/arp.h
--- v1.3.41/linux/include/net/arp.h	Wed Aug  9 14:55:43 1995
+++ linux/include/net/arp.h	Tue Nov 14 16:03:57 1995
@@ -3,19 +3,15 @@
 #define _ARP_H
 
 extern void	arp_init(void);
-extern void	arp_destroy(u32 paddr, int force);
-extern void	arp_device_down(struct device *dev);
 extern int	arp_rcv(struct sk_buff *skb, struct device *dev,
 			struct packet_type *pt);
-extern int	arp_query(unsigned char *haddr, u32 paddr, unsigned short type);
+extern int	arp_query(unsigned char *haddr, u32 paddr, struct device *dev);
 extern int	arp_find(unsigned char *haddr, u32 paddr,
 		struct device *dev, u32 saddr, struct sk_buff *skb);
-extern int	arp_get_info(char *buffer, char **start, off_t origin, int length, int dummy);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
 extern void     arp_send(int type, int ptype, u32 dest_ip, 
 			 struct device *dev, u32 src_ip, 
-			 unsigned char *dest_hw, unsigned char *src_hw);
-extern int	arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev);
-
-extern unsigned long	arp_cache_stamp;
+			 unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
+extern int	arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr);
+extern int	arp_update_cache(struct hh_cache * hh);
 #endif	/* _ARP_H */
diff -u --recursive --new-file v1.3.41/linux/include/net/ip.h linux/include/net/ip.h
--- v1.3.41/linux/include/net/ip.h	Sun Oct 29 11:38:49 1995
+++ linux/include/net/ip.h	Thu Nov 16 09:20:47 1995
@@ -25,6 +25,7 @@
 #include <linux/socket.h>
 #include <linux/ip.h>
 #include <linux/netdevice.h>
+#include <net/route.h>
 
 #ifndef _SNMP_H
 #include <net/snmp.h>
@@ -84,13 +85,13 @@
 extern void		ip_print(const struct iphdr *ip);
 extern int		ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void		ip_route_check(__u32 daddr); 
-extern int 		ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
+extern int 		ip_send(struct rtable *rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
 extern int 		ip_build_header(struct sk_buff *skb,
 					__u32 saddr,
 					__u32 daddr,
 					struct device **dev, int type,
 					struct options *opt, int len,
-					int tos,int ttl);
+					int tos,int ttl,struct rtable **rp);
 extern int		ip_rcv(struct sk_buff *skb, struct device *dev,
 			       struct packet_type *pt);
 extern int		ip_options_echo(struct options * dopt, struct options * sopt,
diff -u --recursive --new-file v1.3.41/linux/include/net/netlink.h linux/include/net/netlink.h
--- v1.3.41/linux/include/net/netlink.h	Sat Nov 11 17:41:34 1995
+++ linux/include/net/netlink.h	Thu Nov 16 09:27:58 1995
@@ -1,11 +1,11 @@
 #ifndef __NET_NETLINK_H
 #define __NET_NETLINK_H
 
-#include <linux/config.h>
-
 #define NET_MAJOR 18		/* Major 18 is reserved for networking 		*/
 #define MAX_LINKS 3		/* 18,0 for route updates, 18,1 for SKIP 	*/
 #define MAX_QBYTES 32768	/* Maximum bytes in the queue 			*/
+
+#include <linux/config.h>
 
 extern int netlink_attach(int unit, int (*function)(struct sk_buff *skb));
 extern int netlink_donothing(struct sk_buff *skb);
diff -u --recursive --new-file v1.3.41/linux/include/net/route.h linux/include/net/route.h
--- v1.3.41/linux/include/net/route.h	Wed Sep 13 12:45:33 1995
+++ linux/include/net/route.h	Tue Nov 14 16:45:12 1995
@@ -12,6 +12,11 @@
  * Fixes:
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
  *		Alan Cox	:	Support for TCP parameters.
+ *		Alexey Kuznetsov:	Major changes for new routing code.
+ *
+ *	FIXME:
+ *		Modules stuff is broken at the moment.
+ *		Make atomic ops more generic and hide them in asm/...
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -21,36 +26,203 @@
 #ifndef _ROUTE_H
 #define _ROUTE_H
 
+/*
+ * 0 - no debugging messages
+ * 1 - rare events and bugs situations (default)
+ * 2 - trace mode.
+ */
+#define RT_CACHE_DEBUG		1
+
+#define RT_HASH_DIVISOR	    	256
+#define RT_CACHE_SIZE_MAX    	256
+
+#define RTZ_HASH_DIVISOR	256
+
+#if RT_CACHE_DEBUG >= 2
+#define RTZ_HASHING_LIMIT 0
+#else
+#define RTZ_HASHING_LIMIT 16
+#endif
+
+/*
+ * Maximal time to live for unused entry.
+ */
+#define RT_CACHE_TIMEOUT		(HZ*300)
+
+/*
+ * Prevents LRU trashing, entries considered equivalent,
+ * if the difference between last use times is less then this number.
+ */
+#define RT_CACHE_BUBBLE_THRESHOULD	(HZ*5)
 
 #include <linux/route.h>
 
+#ifdef __KERNEL__
+#define RTF_LOCAL 0x8000
+#endif
+
+/*
+ * Semaphores.
+ */
+
+#ifdef __i386__
+#include <asm/bitops.h>
+
+extern __inline__ void ATOMIC_INCR(void * addr)
+{
+	__asm__ __volatile__(
+		"incl %0"
+		:"=m" (ADDR));
+}
+
+extern __inline__ void ATOMIC_DECR(void * addr)
+{
+	__asm__ __volatile__(
+		"decl %0"
+		:"=m" (ADDR));
+}
+
+/*
+ * It is DECR that is ATOMIC, not CHECK!
+ * If you want to do atomic checks, use cli()/sti(). --ANK
+ */
+
+extern __inline__ unsigned long ATOMIC_DECR_AND_CHECK(void * addr)
+{
+	unsigned long retval;
+	__asm__ __volatile__(
+		"decl %0\nmovl %0,%1"
+		: "=m" (ADDR), "=r"(retval));
+	return retval;
+}
+
+
+#else
+
+static __inline__ void ATOMIC_INCR(void * addr)
+{
+	(*(__volatile__ unsigned long*)addr)++;
+}
+
+static __inline__ void ATOMIC_DECR(void * addr)
+{
+	(*(__volatile__ unsigned long*)addr)--;
+}
+
+static __inline__ int ATOMIC_DECR_AND_CHECK (void * addr)
+{
+	ATOMIC_DECR(addr);
+	return *(volatile unsigned long*)addr;
+}
+
+#endif
+
+
 
-/* This is an entry in the IP routing table. */
 struct rtable 
 {
 	struct rtable		*rt_next;
 	__u32			rt_dst;
-	__u32			rt_mask;
+	__u32			rt_src;
 	__u32			rt_gateway;
-	unsigned short		rt_flags;
-	short			rt_metric;
-	unsigned int		rt_refcnt;
+	unsigned long		rt_refcnt;
 	unsigned long		rt_use;
-	unsigned short		rt_mss;
-	unsigned short		rt_irtt;
 	unsigned long		rt_window;
+	unsigned long		rt_lastuse;
+	struct hh_cache		*rt_hh;
 	struct device		*rt_dev;
+	unsigned short		rt_flags;
+	unsigned short		rt_mtu;
+	unsigned short		rt_irtt;
+	unsigned char		rt_tos;
 };
 
-
 extern void		ip_rt_flush(struct device *dev);
-extern void		ip_rt_add(short flags, __u32 addr, __u32 mask,
-			       __u32 gw, struct device *dev, unsigned short mss, unsigned long window, unsigned short irtt, short metric);
-extern struct rtable	*ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr);
-extern struct rtable 	*ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr);
+extern void		ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
+extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
+extern int		rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
+extern int		ip_rt_new(struct rtentry *rt);
+extern void		ip_rt_check_expire(void);
+extern void		ip_rt_advice(struct rtable **rp, int advice);
+
+extern void		ip_rt_run_bh(void);
+extern int	    	ip_rt_lock;
+extern unsigned		ip_rt_bh_mask;
+extern struct rtable 	*ip_rt_hash_table[RT_HASH_DIVISOR];
+
+extern __inline__ void ip_rt_fast_lock(void)
+{
+	ATOMIC_INCR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_fast_unlock(void)
+{
+	ATOMIC_DECR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_unlock(void)
+{
+	if (!ATOMIC_DECR_AND_CHECK(&ip_rt_lock) && ip_rt_bh_mask)
+		ip_rt_run_bh();
+}
+
+extern __inline__ unsigned ip_rt_hash_code(__u32 addr)
+{
+	unsigned tmp = addr + (addr>>16);
+	return (tmp + (tmp>>8)) & 0xFF;
+}
+
+
+extern __inline__ void ip_rt_put(struct rtable * rt)
+#ifndef MODULE
+{
+	if (rt)
+		ATOMIC_DECR(&rt->rt_refcnt);
+}
+#else
+;
+#endif
+
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+#ifndef MODULE
+{
+	struct rtable * rth;
+
+	ip_rt_fast_lock();
+
+	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
+	{
+		if (rth->rt_dst == daddr)
+		{
+			rth->rt_lastuse = jiffies;
+			ATOMIC_INCR(&rth->rt_use);
+			ATOMIC_INCR(&rth->rt_refcnt);
+			ip_rt_unlock();
+			return rth;
+		}
+	}
+	return ip_rt_slow_route (daddr, local);
+}
+#else
+;
+#endif
+
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
+						       __u32 daddr, int local)
+{
+	struct rtable * rt = *rp;
+
+	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
+	{
+		ip_rt_put(rt);
+		rt = ip_rt_route(daddr, local);
+		*rp = rt;
+	}
+	return rt;
+}	
 
-extern unsigned long	rt_stamp;
 
 #endif	/* _ROUTE_H */
diff -u --recursive --new-file v1.3.41/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.41/linux/include/net/sock.h	Thu Nov  9 11:23:56 1995
+++ linux/include/net/sock.h	Thu Nov 16 09:20:47 1995
@@ -37,6 +37,7 @@
 #include <linux/tcp.h>		/* struct tcphdr */
 #include <linux/config.h>
 
+#include <linux/netdevice.h>
 #include <linux/skbuff.h>	/* struct sk_buff */
 #include <net/protocol.h>		/* struct inet_protocol */
 #ifdef CONFIG_AX25
@@ -226,19 +227,6 @@
 	struct timer_list	ack_timer;		/* TCP delayed ack timer */
 	int			ip_xmit_timeout;	/* Why the timeout is running */
 	struct rtable		*ip_route_cache;	/* Cached output route */
-	unsigned long		ip_route_stamp;		/* Route cache stamp */
-	unsigned long		ip_route_daddr;		/* Target address */
-	unsigned long		ip_route_saddr;		/* Source address */
-	int			ip_route_local;		/* State of locality flag */
-	unsigned long		ip_hcache_stamp;	/* Header cache stamp */
-	unsigned long 		*ip_hcache_ver;		/* Pointer to version of cache */
-	char			ip_hcache_data[16];	/* Cached header */
-	int			ip_hcache_state;	/* Have we a cached header */
-	unsigned char		ip_option_len;		/* Length of IP options */
-	unsigned char		ip_option_flen;		/* Second fragment option length */
-	unsigned char		ip_opt_next_strict;	/* Next hop is strict route */
-	unsigned long		ip_opt_next_hop;	/* Next hop if forced */
-	unsigned char 		*ip_opt_ptr[2];		/* IP option pointers */
 	unsigned char		ip_hdrincl;		/* Include headers ? */
 #ifdef CONFIG_IP_MULTICAST  
 	int			ip_mc_ttl;		/* Multicasting TTL */
@@ -286,7 +274,7 @@
 					__u32 daddr,
 					struct device **dev, int type,
 					struct options *opt, int len,
-					int tos, int ttl);
+					int tos, int ttl, struct rtable ** rp);
 	int			(*connect)(struct sock *sk,
 				        struct sockaddr_in *usin, int addr_len);
 	struct sock *		(*accept) (struct sock *sk, int flags);
diff -u --recursive --new-file v1.3.41/linux/init/main.c linux/init/main.c
--- v1.3.41/linux/init/main.c	Wed Nov  8 07:11:43 1995
+++ linux/init/main.c	Tue Nov 14 16:03:57 1995
@@ -2,6 +2,8 @@
  *  linux/init/main.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  GK 2/5/95  -  Changed to support mounting root fs via NFS
  */
 
 #define __KERNEL_SYSCALLS__
@@ -26,6 +28,7 @@
 #include <linux/ioport.h>
 #include <linux/hdreg.h>
 #include <linux/mm.h>
+#include <linux/major.h>
 
 #include <asm/bugs.h>
 
@@ -61,7 +64,7 @@
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
-extern void buslogic_setup(char *str, int *ints);
+extern void BusLogic_Setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
@@ -118,6 +121,11 @@
 int ramdisk_size;
 int root_mountflags = MS_RDONLY;
 
+#ifdef CONFIG_ROOT_NFS
+char nfs_root_name[256] = { 0, };
+extern int nfs_root_init(char *name);
+#endif
+
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 
@@ -204,7 +212,7 @@
 	{ "aic7xxx=", aic7xxx_setup},
 #endif
 #ifdef CONFIG_SCSI_BUSLOGIC
-	{ "buslogic=", buslogic_setup},
+	{ "BusLogic=", BusLogic_Setup},
 #endif
 #ifdef CONFIG_SCSI_NCR53C406A
 	{ "ncr53c406a=", NCR53c406a_setup},
@@ -297,9 +305,8 @@
 	int ticks;
 	int loopbit;
 	int lps_precision = LPS_PREC;
-#ifdef __SMP__
+
 	loops_per_sec = (1<<12);
-#endif		
 
 	printk("Calibrating delay loop.. ");
 	while (loops_per_sec <<= 1) {
@@ -350,10 +357,14 @@
 static void parse_options(char *line)
 {
 	char *next;
+#ifdef CONFIG_ROOT_NFS
+	char *devnames[] = { "nfs", "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL };
+	int devnums[]    = { 0x0FF, 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0};
+#else
 	static const char *devnames[] = { "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL };
 	static int devnums[]    = { 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0};
+#endif
 	int args, envs;
-
 	if (!*line)
 		return;
 	args = 0;
@@ -384,6 +395,20 @@
 			}
 			continue;
 		}
+#ifdef CONFIG_ROOT_NFS
+		if (!strncmp(line,"nfsroot=",8)) {
+			int n = 255 - strlen (NFS_ROOT);
+			line += 8;
+			if (line [0] == '/' || (line [0] >= '0' && line [0] <= '9')){
+				strncpy (nfs_root_name, line, 255);
+				continue;
+			}
+			if (strlen (line) >= n)
+			        line [n] = 0;
+			sprintf (nfs_root_name, NFS_ROOT, line);
+			continue;
+		}
+#endif
 		if (!strcmp(line,"ro")) {
 			root_mountflags |= MS_RDONLY;
 			continue;
diff -u --recursive --new-file v1.3.41/linux/kernel/exit.c linux/kernel/exit.c
--- v1.3.41/linux/kernel/exit.c	Wed Nov  8 07:11:43 1995
+++ linux/kernel/exit.c	Tue Nov 14 08:42:27 1995
@@ -332,7 +332,7 @@
 	return(1);	/* (sighing) "Often!" */
 }
 
-static int has_stopped_jobs(int pgrp)
+static inline int has_stopped_jobs(int pgrp)
 {
 	struct task_struct * p;
 
@@ -345,7 +345,7 @@
 	return(0);
 }
 
-static void forget_original_parent(struct task_struct * father)
+static inline void forget_original_parent(struct task_struct * father)
 {
 	struct task_struct * p;
 
@@ -358,7 +358,7 @@
 	}
 }
 
-void exit_files(struct task_struct *tsk)
+static inline void __exit_files(struct task_struct *tsk)
 {
 	struct files_struct * files = tsk->files;
 
@@ -378,7 +378,12 @@
 	}
 }
 
-void exit_fs(struct task_struct *tsk)
+void exit_files(struct task_struct *tsk)
+{
+  __exit_files(tsk);
+}
+
+static inline void __exit_fs(struct task_struct *tsk)
 {
 	struct fs_struct * fs = tsk->fs;
 
@@ -392,7 +397,12 @@
 	}
 }
 
-void exit_sighand(struct task_struct *tsk)
+void exit_fs(struct task_struct *tsk)
+{
+  __exit_fs(tsk);
+}
+
+static inline void __exit_sighand(struct task_struct *tsk)
 {
 	struct signal_struct * sig = tsk->sig;
 
@@ -404,7 +414,12 @@
 	}
 }
 
-static void exit_mm(void)
+void exit_sighand(struct task_struct *tsk)
+{
+  __exit_sighand(tsk);
+}
+
+static inline void exit_mm(void)
 {
 	struct mm_struct * mm = current->mm;
 
@@ -497,9 +512,9 @@
 	del_timer(&current->real_timer);
 	sem_exit();
 	exit_mm();
-	exit_files(current);
-	exit_fs(current);
-	exit_sighand(current);
+	__exit_files(current);
+	__exit_fs(current);
+	__exit_sighand(current);
 	exit_thread();
 	exit_notify();
 	current->state = TASK_ZOMBIE;
@@ -544,6 +559,11 @@
 		if (flag)
 			return flag;
 	}
+	if (ru) {
+		flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru));
+		if (flag)
+			return flag;
+	}
 	add_wait_queue(&current->wait_chldexit,&wait);
 repeat:
 	flag=0;
@@ -568,12 +588,12 @@
 					continue;
 				if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
 					continue;
+				if (ru != NULL)
+					getrusage(p, RUSAGE_BOTH, ru);
 				if (stat_addr)
 					put_user((p->exit_code << 8) | 0x7f,
 						stat_addr);
 				p->exit_code = 0;
-				if (ru != NULL)
-					getrusage(p, RUSAGE_BOTH, ru);
 				retval = p->pid;
 				goto end_wait4;
 			case TASK_ZOMBIE:
@@ -581,9 +601,9 @@
 				current->cstime += p->stime + p->cstime;
 				if (ru != NULL)
 					getrusage(p, RUSAGE_BOTH, ru);
-				flag = p->pid;
 				if (stat_addr)
 					put_user(p->exit_code, stat_addr);
+				retval = p->pid;
 				if (p->p_opptr != p->p_pptr) {
 					REMOVE_LINKS(p);
 					p->p_pptr = p->p_opptr;
@@ -594,7 +614,6 @@
 #ifdef DEBUG_PROC_TREE
 				audit_ptree();
 #endif
-				retval = flag;
 				goto end_wait4;
 			default:
 				continue;
diff -u --recursive --new-file v1.3.41/linux/kernel/fork.c linux/kernel/fork.c
--- v1.3.41/linux/kernel/fork.c	Wed Nov  8 07:11:43 1995
+++ linux/kernel/fork.c	Tue Nov 14 08:42:14 1995
@@ -29,7 +29,7 @@
 int nr_tasks=1;
 int nr_running=1;
 
-static int find_empty_process(void)
+static inline int find_empty_process(void)
 {
 	int i;
 	struct task_struct *p;
@@ -75,7 +75,7 @@
 	return last_pid;
 }
 
-static int dup_mmap(struct mm_struct * mm)
+static inline int dup_mmap(struct mm_struct * mm)
 {
 	struct vm_area_struct * mpnt, **p, *tmp;
 
@@ -110,7 +110,7 @@
 	return 0;
 }
 
-static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
 {
 	if (clone_flags & CLONE_VM) {
 		SET_PAGE_DIR(tsk, current->mm->pgd);
@@ -134,7 +134,7 @@
 	return 0;
 }
 
-static int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
 {
 	if (clone_flags & CLONE_FS) {
 		current->fs->count++;
@@ -152,7 +152,7 @@
 	return 0;
 }
 
-static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk)
 {
 	int i;
 
@@ -175,7 +175,7 @@
 	return 0;
 }
 
-static int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
 {
 	if (clone_flags & CLONE_SIGHAND) {
 		current->sig->count++;
diff -u --recursive --new-file v1.3.41/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.41/linux/kernel/ksyms.c	Mon Nov 13 12:36:46 1995
+++ linux/kernel/ksyms.c	Wed Nov 15 08:09:31 1995
@@ -126,7 +126,7 @@
 
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
-#ifdef CONFIG_MODVERSIONS
+#ifdef MODVERSIONS
 	{ (void *)1 /* Version version :-) */,
 		SYMBOL_NAME_STR (Using_Versions) },
 #endif
@@ -226,7 +226,8 @@
 	X(getblk),
 	X(bread),
 	X(breada),
-	X(brelse),
+	X(__brelse),
+	X(__bforget),
 	X(ll_rw_block),
 	X(__wait_on_buffer),
 	X(dcache_lookup),
@@ -387,6 +388,7 @@
 	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		
 	/* If 8390 NIC support is built in, we will need these. */
 	X(ei_open),
+	X(ei_close),
 	X(ei_debug),
 	X(ei_interrupt),
 	X(ethdev_init),
diff -u --recursive --new-file v1.3.41/linux/kernel/signal.c linux/kernel/signal.c
--- v1.3.41/linux/kernel/signal.c	Sun Oct 29 11:38:49 1995
+++ linux/kernel/signal.c	Tue Nov 14 08:42:20 1995
@@ -90,7 +90,7 @@
  * isn't actually ignored, but does automatic child reaping, while
  * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
  */
-static void check_pending(int signum)
+static inline void check_pending(int signum)
 {
 	struct sigaction *p;
 
diff -u --recursive --new-file v1.3.41/linux/mm/mmap.c linux/mm/mmap.c
--- v1.3.41/linux/mm/mmap.c	Thu Nov  9 11:23:56 1995
+++ linux/mm/mmap.c	Tue Nov 14 08:51:27 1995
@@ -17,7 +17,16 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 
-static int anon_map(struct inode *, struct file *, struct vm_area_struct *);
+/*
+ * Map memory not associated with any file into a process
+ * address space.  Adjacent memory is merged.
+ */
+static inline int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma)
+{
+	if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -ENOMEM;
+	return 0;
+}
 
 /*
  * description of effects of mapping type and prot in current implementation.
@@ -212,7 +221,6 @@
  *   vm_avl_height   1+max(heightof(left),heightof(right))
  * The empty tree is represented as NULL.
  */
-#define avl_empty	(struct vm_area_struct *) NULL
 
 /* Since the trees are balanced, their height will never be large. */
 #define avl_maxheight	41	/* why this? a small exercise */
@@ -225,64 +233,8 @@
  *    foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.
  */
 
-/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
-struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
-{
-#if 0 /* equivalent, but slow */
-	struct vm_area_struct * vma;
-
-	if (!task->mm)
-		return NULL;
-	for (vma = task->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			return NULL;
-		if (vma->vm_end > addr)
-			return vma;
-	}
-#else
-	struct vm_area_struct * result = NULL;
-	struct vm_area_struct * tree;
-
-	if (!task->mm)
-		return NULL;
-	for (tree = task->mm->mmap_avl ; ; ) {
-		if (tree == avl_empty)
-			return result;
-		if (tree->vm_end > addr) {
-			if (tree->vm_start <= addr)
-				return tree;
-			result = tree;
-			tree = tree->vm_avl_left;
-		} else
-			tree = tree->vm_avl_right;
-	}
-#endif
-}
-
-/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
-   NULL if none.  Assume start_addr < end_addr. */
-struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
-{
-	struct vm_area_struct * vma;
-
-#if 0 /* equivalent, but slow */
-	for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
-		if (end_addr <= vma->vm_start)
-			break;
-		if (start_addr < vma->vm_end)
-			return vma;
-	}
-	return NULL;
-#else
-	vma = find_vma(task,start_addr);
-	if (!vma || end_addr <= vma->vm_start)
-		return NULL;
-	return vma;
-#endif
-}
-
 /* Look up the nodes at the left and at the right of a given node. */
-static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+static inline void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
 {
 	vm_avl_key_t key = node->vm_avl_key;
 
@@ -328,7 +280,7 @@
  * nodes[0]..nodes[k-1] such that
  * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}.
  */
-static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
+static inline void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
 {
 	for ( ; count > 0 ; count--) {
 		struct vm_area_struct ** nodeplace = *--nodeplaces_ptr;
@@ -405,7 +357,7 @@
 }
 
 /* Insert a node into a tree. */
-static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
+static inline void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
 {
 	vm_avl_key_t key = new_node->vm_avl_key;
 	struct vm_area_struct ** nodeplace = ptree;
@@ -432,7 +384,7 @@
 /* Insert a node into a tree, and
  * return the node to the left of it and the node to the right of it.
  */
-static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
+static inline void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
 	struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
 {
 	vm_avl_key_t key = new_node->vm_avl_key;
@@ -462,7 +414,7 @@
 }
 
 /* Removes a node out of a tree. */
-static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
+static inline void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
 {
 	vm_avl_key_t key = node_to_delete->vm_avl_key;
 	struct vm_area_struct ** nodeplace = ptree;
@@ -957,15 +909,4 @@
 		kfree_s(mpnt, sizeof(*mpnt));
 		mpnt = prev;
 	}
-}
-
-/*
- * Map memory not associated with any file into a process
- * address space.  Adjacent memory is merged.
- */
-static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma)
-{
-	if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
-		return -ENOMEM;
-	return 0;
 }
diff -u --recursive --new-file v1.3.41/linux/net/Changes linux/net/Changes
--- v1.3.41/linux/net/Changes	Wed Nov  8 07:11:43 1995
+++ linux/net/Changes	Tue Nov 14 17:22:04 1995
@@ -262,7 +262,7 @@
 o	Recvmsg for all IP, sendmsg for TCP		[IN]
 	(nearly ready to go all *msg())
 
--------->>>>> 1.3.38 <<<<<<--------
+-------->>>>> 1.3.41 <<<<<<--------
 
 o	ip udp/raw nonblock bug fixed			[TESTED]
 o	ICMP lockup fix					[IN]
@@ -271,7 +271,15 @@
 o	set_mac_addr fixed up				[IN]
 o	BSD SIOCSIFADDR, AF_UNSPEC behaviour		[IN]
 o	Updated this list				[IN]
-
+o	Massive ARP/cache/routing rewrite [ANK]		[IN]
+o	AX.25 connect return fixed in using sock_error	[IN]
+o	Proper netlink device major(36)			[IN]
+o	First parts of the SKIP support			[IN, not useful]
+o	TCP ICMP (SOSS should work again)		[IN]
+o	IPFW support for TOS changing (Al Longyear)	[IN]
+o	DECNET PPP test code				[IN]
+o	NFS root					[IN]
+o	Path MTU discovery [ANK]			[IN]
 
 ---------- Things I thought Linus had for a while and not merged ----------------
 
@@ -280,33 +288,29 @@
 
 o	Improved IPX support for lwared.
 o	Decnet pre pre pre pre pre Alpha 0.0.
-o	Faster routing/caching support.
 
 ---------- Things pending for me to merge --------------
 
-o	IPFW support for TOS changing (Al Longyear)
 o	AF_UNIX garbage collect code
 o	Faster closedown option for heavy use sites (me)
 o	Tom May's insw_and_checksum()			
 o	IPX PPP support
-o	DECNET PPP test code
-o	SPARC patches [Dave]
+o	SPARC patches [Dave] [partly in]
+o	Path MTU and other goodies [ANK]
 
 --------------- Things That Need Doing Before 1.4 ------------------
 
-o	inet_error for other layers
 o	Finish merging the bridge code
 o	Fast checksum/copy on outgoing TCP
 o	Fast dev_grab_next() transmit reload function
-	and dev_push_failed() ??
-o	Faster ip_forward last hit cache		[PENDING(GuruA0)]
-o	L2 ip routing cache				[PENDING(btv)]
+	and dev_push_failed() ??			[Causes deadlock
+							cases with slow box, heavy networking]
 o	Forwarding queue control (+ fairness algorithms ??)
 o	IP forward flow control.
 o	Clean up RAW AX.25 sockets.
 o	Finish 802.2 Class I code to be compliant to the oddities of 802.2
 o	Tidy BPQ support to use a bpqip tunnel device
-o	Strange eth0-eth3 bug 
+o	Strange eth0-eth3 bug 				[Matti fixed ??]
 o	Finish IPIP bug fixes				[Done hopefully]
 o	Why doesnt the PROTO_UNREACH get sent ?		[Should now work]
 o	Kill off old ip_queue_xmit/ip_send stuff.
diff -u --recursive --new-file v1.3.41/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.41/linux/net/ax25/af_ax25.c	Wed Nov  8 07:11:44 1995
+++ linux/net/ax25/af_ax25.c	Tue Nov 14 16:03:57 1995
@@ -661,7 +661,7 @@
 						return dev;
 					break;
 				case ARPHRD_ETHER:
-					if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, ARPHRD_AX25))
+					if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, dev))
 						if (ax25cmp(addr, &dev_addr) == 0)
 							return dev;
 					break;
@@ -1669,7 +1669,7 @@
 
 	skb->sk = NULL;		/* Initially we don't know who its for */
 
-	if (!arp_query((unsigned char *)&port_call, dev->pa_addr, ARPHRD_AX25)) {
+	if (!arp_query((unsigned char *)&port_call, dev->pa_addr, dev)) {
 		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
 		return 0;
 	}
diff -u --recursive --new-file v1.3.41/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.41/linux/net/core/dev.c	Wed Nov  8 07:11:45 1995
+++ linux/net/core/dev.c	Tue Nov 14 16:03:57 1995
@@ -39,6 +39,8 @@
  *		Alan Cox	:	Device lock protection.
  *		Alan Cox	: 	Fixed nasty side effect of device close changes.
  *		Rudi Cilibrasi	:	Pass the right thing to set_mac_address()
+ *		Dave Miller	:	32bit quantity for the device lock to make it work out
+ *					on a Sparc.
  *
  */
 
diff -u --recursive --new-file v1.3.41/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v1.3.41/linux/net/ethernet/eth.c	Tue Oct 10 18:46:39 1995
+++ linux/net/ethernet/eth.c	Thu Nov 16 08:23:49 1995
@@ -89,7 +89,7 @@
 int eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
 	   void *daddr, void *saddr, unsigned len)
 {
-	struct ethhdr *eth = (struct ethhdr *)skb_push(skb,14);
+	struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
 
 	/* 
 	 *	Set the protocol type. For a packet of type ETH_P_802_3 we put the length
@@ -175,7 +175,7 @@
 	unsigned char *rawp;
 	
 	skb->mac.raw=skb->data;
-	skb_pull(skb,14);	
+	skb_pull(skb,dev->hard_header_len);
 	eth= skb->mac.ethernet;
 	
 	if(*eth->h_dest&1)
@@ -218,28 +218,48 @@
 }
 
 /*
- *	Header caching for ethernet. Try to find and cache a header to avoid arp overhead.
+ * Upper level calls this function to bind hardware header cache entry.
+ * If the call is successful, then corresponding Address Resolution Protocol
+ * (maybe, not ARP) takes responsibility for updating cache content.
  */
- 
-void eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr)
+
+void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
+			   unsigned short htype, __u32 daddr)
 {
-	int v=arp_find_cache(sk->ip_hcache_data, daddr, dev);
-	if(v!=1)
-		sk->ip_hcache_state=0;	/* Try when arp resolves */
-	else
+	struct hh_cache *hh;
+
+	if (htype != ETH_P_IP)
+	{
+		printk("eth_header_cache_bind: %04x cache is not implemented\n", htype);
+		return;
+	}
+	if (arp_bind_cache(hhp, dev, htype, daddr))
+		return;
+	if ((hh=*hhp) != NULL)
+	{
+		memcpy(hh->hh_data+6, dev->dev_addr, ETH_ALEN);
+		hh->hh_data[12] = htype>>8;
+		hh->hh_data[13] = htype&0xFF;
+	}
+}
+
+/*
+ * Called by Address Resolution module to notify changes in address.
+ */
+
+void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
+{
+	if (hh->hh_type != ETH_P_IP)
 	{
-		memcpy(sk->ip_hcache_data+6, dev->dev_addr, ETH_ALEN);
-		sk->ip_hcache_data[12]=ETH_P_IP>>8;
-		sk->ip_hcache_data[13]=ETH_P_IP&0xFF;
-		sk->ip_hcache_state=1;
-		sk->ip_hcache_stamp=arp_cache_stamp;
-		sk->ip_hcache_ver=&arp_cache_stamp;
+		printk("eth_header_cache_update: %04x cache is not implemented\n", hh->hh_type);
+		return;
 	}
+	memcpy(hh->hh_data, haddr, ETH_ALEN);
+	hh->hh_uptodate = 1;
 }
 
 /*
  *	Copy from an ethernet device memory space to an sk_buff while checksumming if IP
- *	The magic "34" is Rx_addr+Tx_addr+type_field+sizeof(struct iphdr) == 6+6+2+20.
  */
  
 void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base)
@@ -261,13 +281,15 @@
 	 * We have to use the smaller of length and ip_length because it
 	 * can happen that ip_length > length.
 	 */
-	memcpy(dest->data,src,34);	/* ethernet is always >= 34 */
-	length -= 34;
-	iph=(struct iphdr*)(src+14);	/* 14 = Rx_addr+Tx_addr+type_field */
+	memcpy(dest->data,src,sizeof(struct iphdr)+ETH_HLEN);	/* ethernet is always >= 34 */
+	length -= sizeof(struct iphdr) + ETH_HLEN;
+	iph=(struct iphdr*)(src+ETH_HLEN);
 	ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr);
-	if (ip_length <= length)
+
+	/* Also watch out for bogons - min IP size is 8 (rfc-1042) */
+	if ((ip_length <= length) && (ip_length > 7))
 		length=ip_length;
 
-	dest->csum=csum_partial_copy(src+34,dest->data+34,length,base);
+	dest->csum=csum_partial_copy(src+sizeof(struct iphdr)+ETH_HLEN,dest->data+sizeof(struct iphdr)+ETH_HLEN,length,base);
 	dest->ip_summed=1;
 }
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v1.3.41/linux/net/ipv4/Config.in	Thu Nov  9 11:23:56 1995
+++ linux/net/ipv4/Config.in	Tue Nov 14 16:03:57 1995
@@ -20,7 +20,7 @@
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
-bool 'IP: Assume subnets are local' CONFIG_INET_SNARL
+bool 'IP: Disable Path MTU Discovery (normally enabled)' CONFIG_NO_PATH_MTU_DISCOVERY
 bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
 bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
 bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.41/linux/net/ipv4/af_inet.c	Wed Nov  8 07:11:45 1995
+++ linux/net/ipv4/af_inet.c	Tue Nov 14 16:03:57 1995
@@ -402,6 +402,7 @@
 	{
 		if(sk->opt)
 			kfree(sk->opt);
+		ip_rt_put(sk->ip_route_cache);
 		/*
 		 *	This one is pure paranoia. I'll take it out
 		 *	later once I know the bug is buried.
@@ -915,6 +916,7 @@
 		sk->daddr = 0;
 		sk->dummy_th.dest = 0;
 	}
+	ip_rt_put(sk->ip_route_cache);
 	sk->ip_route_cache=NULL;
 	return(0);
 }
@@ -1236,6 +1238,9 @@
 		case SIOCDARP:
 		case SIOCGARP:
 		case SIOCSARP:
+		case OLD_SIOCDARP:
+		case OLD_SIOCGARP:
+		case OLD_SIOCSARP:
 			return(arp_ioctl(cmd,(void *) arg));
 		case SIOCDRARP:
 		case SIOCGRARP:
@@ -1569,5 +1574,11 @@
 		S_IFREG | S_IRUGO, 1, 0, 0,
 		0, &proc_net_inode_operations,
 		rt_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RTCACHE, 8, "rt_cache",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		rt_cache_get_info
 	});
 }
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.41/linux/net/ipv4/arp.c	Wed Nov  8 07:11:45 1995
+++ linux/net/ipv4/arp.c	Tue Nov 14 16:03:57 1995
@@ -105,12 +105,11 @@
 {
 	struct arp_table		*next;			/* Linked entry list 		*/
 	unsigned long			last_used;		/* For expiry 			*/
+	unsigned long			last_updated;		/* For expiry 			*/
 	unsigned int			flags;			/* Control status 		*/
 	u32				ip;			/* ip address of entry 		*/
 	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
 	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
-	unsigned char			hlen;			/* Length of hardware address 	*/
-	unsigned short			htype;			/* Type of hardware in use	*/
 	struct device			*dev;			/* Device the entry is tied to 	*/
 
 	/*
@@ -120,6 +119,7 @@
 	struct timer_list		timer;			/* expire timer 		*/
 	int				retries;		/* remaining retries	 	*/
 	struct sk_buff_head		skb;			/* list of queued packets 	*/
+	struct hh_cache			*hh;
 };
 
 
@@ -130,17 +130,18 @@
 /*
  *	If an arp request is send, ARP_RES_TIME is the timeout value until the
  *	next request is send.
+ * 	RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
+ *	The recommended minimum timeout is 1 second per destination.
+ *	This timeout is prolongated to ARP_DEAD_RES_TIME, if
+ *	destination does not respond.
  */
 
-/* RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST) */
-/* The recommended minimum timeout is 1 second per destination. */
-/* Is this a per-destination timeout? -- MS [YES AC]*/
-
-#define ARP_RES_TIME		(250*(HZ/10))
+#define ARP_RES_TIME		(5*HZ)
+#define ARP_DEAD_RES_TIME	(60*HZ)
 
 /*
  *	The number of times an arp request is send, until the host is
- *	considered unreachable.
+ *	considered temporarily unreachable.
  */
 
 #define ARP_MAX_TRIES		3
@@ -153,22 +154,30 @@
 
 /*
  *	How often is the function 'arp_check_retries' called.
- *	An entry is invalidated in the time between ARP_TIMEOUT and
+ *	An unused entry is invalidated in the time between ARP_TIMEOUT and
  *	(ARP_TIMEOUT+ARP_CHECK_INTERVAL).
  */
 
-#define ARP_CHECK_INTERVAL	(60 * HZ)
+#define ARP_CHECK_INTERVAL	(60*HZ)
 
-enum proxy {
-   PROXY_EXACT=0,
-   PROXY_ANY,
-   PROXY_NONE,
-};
+/*
+ *	The entry is reconfirmed by sending point-to-point ARP
+ *	request after ARP_CONFIRM_INTERVAL. If destinations does not respond
+ *	for ARP_CONFIRM_TIMEOUT, normal broadcast resolution scheme is started.
+ */
 
-/* Forward declarations. */
-static void arp_check_expire (unsigned long);  
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type);
+#define ARP_CONFIRM_INTERVAL	(300*HZ)
+#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
+
+static unsigned long arp_lock;
+static unsigned long arp_bh_mask;
+
+#define ARP_BH_BACKLOG	1
+
+static struct arp_table *arp_backlog;
 
+static void arp_run_bh(void);
+static void arp_check_expire (unsigned long);  
 
 static struct timer_list arp_timer =
 	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -176,26 +185,17 @@
 /*
  * The default arp netmask is just 255.255.255.255 which means it's
  * a single machine entry. Only proxy entries can have other netmasks
- *
-*/
+ */
 
 #define DEF_ARP_NETMASK (~0)
 
-
 /*
  * 	The size of the hash table. Must be a power of two.
  * 	Maybe we should remove hashing in the future for arp and concentrate
  * 	on Patrick Schaaf's Host-Cache-Lookup...
  */
 
-
 #define ARP_TABLE_SIZE  16
-
-/* The ugly +1 here is to cater for proxy entries. They are put in their 
-   own list for efficiency of lookup. If you don't want to find a proxy
-   entry then don't look in the last entry, otherwise do 
-*/
-
 #define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)
 
 struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
@@ -203,8 +203,7 @@
 	NULL,
 };
 
-unsigned long arp_cache_stamp;
-
+#define arp_proxy_list arp_tables[ARP_TABLE_SIZE]
 
 /*
  *	The last bits in the IP address are used for the cache lookup.
@@ -212,82 +211,356 @@
  */
 
 #define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
-#define PROXY_HASH ARP_TABLE_SIZE
+
+/*
+ * Lock/unlock arp_table chains.
+ */
+
+static __inline__ void arp_fast_lock(void)
+{
+	ATOMIC_INCR(&arp_lock);
+}
+
+static __inline__ void arp_fast_unlock(void)
+{
+	ATOMIC_DECR(&arp_lock);
+}
+
+static __inline__ void arp_unlock(void)
+{
+	if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask)
+		arp_run_bh();
+}
+
+/*
+ * Enqueue to FIFO list.
+ */
+
+static void arp_enqueue(struct arp_table **q, struct arp_table *entry)
+{
+	unsigned long flags;
+	struct arp_table * tail;
+
+	save_flags(flags);
+	cli();
+	tail = *q;
+	if (!tail)
+		entry->next = entry;
+	else
+	{
+		entry->next = tail->next;
+		tail->next = entry;
+	}
+	*q = entry;
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * Dequeue from FIFO list,
+ * caller should mask interrupts.
+ */
+
+static struct arp_table * arp_dequeue(struct arp_table **q)
+{
+	struct arp_table * entry;
+
+	if (*q)
+	{
+		entry = (*q)->next;
+		(*q)->next = entry->next;
+		if (entry->next == entry)
+			*q = NULL;
+		entry->next = NULL;
+		return entry;
+	}
+	return NULL;
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void arp_release_entry(struct arp_table *entry)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* Release the list of `skb' pointers. */
+	while ((skb = skb_dequeue(&entry->skb)) != NULL)
+	{
+		skb_device_lock(skb);
+		restore_flags(flags);
+		dev_kfree_skb(skb, FREE_WRITE);
+		cli();
+	}
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * 	Release the entry and all resources linked to it: skb's, hh's, timer
+ * 	and certainly memory.
+ */
+
+static void arp_free_entry(struct arp_table *entry)
+{
+	unsigned long flags;
+	struct hh_cache *hh, *next;
+
+	del_timer(&entry->timer);
+
+	save_flags(flags);
+	cli();
+	arp_release_entry(entry);
+
+	for (hh = entry->hh; hh; hh = next)
+	{
+		next = hh->hh_next;
+		hh->hh_arp = NULL;
+		if (!--hh->hh_refcnt)
+			kfree_s(hh, sizeof(struct(struct hh_cache)));
+	}
+	restore_flags(flags);
+
+	kfree_s(entry, sizeof(struct arp_table));
+	return;
+}
+
+/*
+ * How many users has this entry?
+ */
+
+static __inline__ int arp_count_hhs(struct arp_table * entry)
+{
+	struct hh_cache *hh, **hhp;
+	int count = 0;
+
+	hhp = &entry->hh;
+	while ((hh=*hhp) != NULL)
+	{
+		if (hh->hh_refcnt == 1)
+		{
+			*hhp = hh->hh_next;
+			kfree_s(hh, sizeof(struct hh_cache));
+			continue;
+		}
+		count += hh->hh_refcnt-1;
+		hhp = &hh->hh_next;
+	}
+
+	return count;
+}
+
+/*
+ * Invalidate all hh's, so that higher level will not try to use it.
+ */
+
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
+{
+	struct hh_cache *hh;
+
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		hh->hh_uptodate = 0;
+}
+
+/*
+ * Signal to device layer, that hardware address may be changed.
+ */
+
+static __inline__ void arp_update_hhs(struct arp_table * entry)
+{
+	struct hh_cache *hh;
+
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
+}
 
 /*
  *	Check if there are too old entries and remove them. If the ATF_PERM
  *	flag is set, they are always left in the arp cache (permanent entry).
- *	Note: Only fully resolved entries, which don't have any packets in
- *	the queue, can be deleted, since ARP_TIMEOUT is much greater than
- *	ARP_MAX_TRIES*ARP_RES_TIME.
+ *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
+ *	declare it invalid and send point-to-point ARP request.
+ *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
+ *	give it to shred by arp_expire_entry.
  */
 
-/* RFC1122: Looks good.  Prevents stale ARP entries, as per 2.3.2.1. (MUST) */
-
 static void arp_check_expire(unsigned long dummy)
 {
 	int i;
 	unsigned long now = jiffies;
-	unsigned long flags;
-	save_flags(flags);
-	cli();
 
-	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
+	del_timer(&arp_timer);
+
+	if (!arp_lock)
 	{
-		struct arp_table *entry;
-		struct arp_table **pentry = &arp_tables[i];
+		arp_fast_lock();
 
-		while ((entry = *pentry) != NULL)
+		for (i = 0; i < ARP_TABLE_SIZE; i++)
 		{
-			if ((now - entry->last_used) > ARP_TIMEOUT
-				&& !(entry->flags & ATF_PERM))
+			struct arp_table *entry;
+			struct arp_table **pentry;
+		
+			pentry = &arp_tables[i];
+
+			while ((entry = *pentry) != NULL)
 			{
-				*pentry = entry->next;	/* remove from list */
-				arp_cache_stamp++;
-				del_timer(&entry->timer);	/* Paranoia */
-				kfree_s(entry, sizeof(struct arp_table)); 
-				/* Don't have to remove packets in entry->skb. */
-				/* See comments above. */
+				cli();
+				if (now - entry->last_used > ARP_TIMEOUT
+				    && !(entry->flags & ATF_PERM)
+				    && !arp_count_hhs(entry))
+				{
+					*pentry = entry->next;
+					sti();
+#if RT_CACHE_DEBUG >= 2
+					printk("arp_expire: %08x expired\n", entry->ip);
+#endif
+					arp_free_entry(entry);
+				}
+				else if (entry->last_updated
+					 && now - entry->last_updated > ARP_CONFIRM_INTERVAL
+					 && !(entry->flags & ATF_PERM))
+				{
+					struct device * dev = entry->dev;
+					pentry = &entry->next;
+					entry->flags &= ~ATF_COM;
+					arp_invalidate_hhs(entry);
+					sti();
+					entry->retries = ARP_MAX_TRIES+1;
+					del_timer(&entry->timer);
+					entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
+					add_timer(&entry->timer);
+					arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
+						 dev, dev->pa_addr, entry->ha,
+						 dev->dev_addr, NULL);
+#if RT_CACHE_DEBUG >= 2
+					printk("arp_expire: %08x requires confirmation\n", entry->ip);
+#endif
+				}
+				else
+					pentry = &entry->next;	/* go to next entry */
 			}
-			else
-				pentry = &entry->next;	/* go to next entry */
 		}
+		arp_unlock();
 	}
-	restore_flags(flags);
+
+	ip_rt_check_expire();
 
 	/*
 	 *	Set the timer again.
 	 */
 
-	del_timer(&arp_timer);
 	arp_timer.expires = jiffies + ARP_CHECK_INTERVAL;
 	add_timer(&arp_timer);
 }
 
-
 /*
- *	Release all linked skb's and the memory for this entry.
+ *	This function is called, if an entry is not resolved in ARP_RES_TIME.
+ *	When more than MAX_ARP_TRIES retries was done, release queued skb's,
+ *	but not discard entry itself if  it is in use.
  */
 
-static void arp_release_entry(struct arp_table *entry)
+static void arp_expire_request (unsigned long arg)
 {
-	struct sk_buff *skb;
+	struct arp_table *entry = (struct arp_table *) arg;
+	struct arp_table **pentry;
+	unsigned long hash;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	/* Release the list of `skb' pointers. */
-	while ((skb = skb_dequeue(&entry->skb)) != NULL)
+
+	/*
+	 *	Since all timeouts are handled with interrupts enabled, there is a
+	 *	small chance, that this entry has just been resolved by an incoming
+	 *	packet. This is the only race condition, but it is handled...
+	 */
+	
+	if (entry->flags & ATF_COM)
 	{
-		skb_device_lock(skb);
 		restore_flags(flags);
-		dev_kfree_skb(skb, FREE_WRITE);
-		cli();
+		return;
 	}
+
+	if (arp_lock)
+	{
+#if RT_CACHE_DEBUG >= 1
+		printk("arp_expire_request: %08x postponed\n", entry->ip);
+#endif
+		del_timer(&entry->timer);
+		entry->timer.expires = jiffies + HZ/10;
+		add_timer(&entry->timer);
+		restore_flags(flags);
+		return;
+	}
+
+	arp_fast_lock();
 	restore_flags(flags);
-	del_timer(&entry->timer);
-	kfree_s(entry, sizeof(struct arp_table));
-	return;
+
+	if (entry->last_updated && --entry->retries > 0)
+	{
+		struct device *dev = entry->dev;
+
+#if RT_CACHE_DEBUG >= 2
+		printk("arp_expire_request: %08x timed out\n", entry->ip);
+#endif
+		/* Set new timer. */
+		del_timer(&entry->timer);
+		entry->timer.expires = jiffies + ARP_RES_TIME;
+		add_timer(&entry->timer);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
+			 NULL, dev->dev_addr, NULL);
+		arp_unlock();
+		return;
+	}
+
+	arp_release_entry(entry);
+
+	cli();
+	if (arp_count_hhs(entry))
+	{
+		struct device *dev = entry->dev;
+#if RT_CACHE_DEBUG >= 2
+		printk("arp_expire_request: %08x is dead\n", entry->ip);
+#endif
+		arp_release_entry(entry);
+		entry->retries = ARP_MAX_TRIES;
+		restore_flags(flags);
+		entry->last_updated = 0;
+		del_timer(&entry->timer);
+		entry->timer.expires = jiffies + ARP_DEAD_RES_TIME;
+		add_timer(&entry->timer);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
+			 NULL, dev->dev_addr, NULL);
+		arp_unlock();
+		return;
+	}
+	restore_flags(flags);
+
+	hash = HASH(entry->ip);
+
+	pentry = &arp_tables[hash];
+
+	while (*pentry != NULL)
+	{
+		if (*pentry == entry)
+		{
+			cli();
+			*pentry = entry->next;
+			restore_flags(flags);
+#if RT_CACHE_DEBUG >= 2
+			printk("arp_expire_request: %08x is killed\n", entry->ip);
+#endif
+			arp_free_entry(entry);
+			arp_unlock();
+			return;
+		}
+		pentry = &(*pentry)->next;
+	}
+	printk("arp_expire_request: bug: ARP entry is lost!\n");
+	arp_unlock();
 }
 
 /*
@@ -298,16 +571,19 @@
 {
 	struct device *dev=ptr;
 	int i;
-	unsigned long flags;
 	
-	if(event!=NETDEV_DOWN)
+	if (event != NETDEV_DOWN)
 		return NOTIFY_DONE;
 	/*
 	 *	This is a bit OTT - maybe we need some arp semaphores instead.
 	 */
-	 
-	save_flags(flags);
-	cli();
+
+#if RT_CACHE_DEBUG >= 1	 
+	if (arp_lock)
+		printk("arp_device_event: bug\n");
+#endif
+	arp_fast_lock();
+
 	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
 	{
 		struct arp_table *entry;
@@ -315,18 +591,15 @@
 
 		while ((entry = *pentry) != NULL)
 		{
-			if(entry->dev==dev)
+			if (entry->dev == dev)
 			{
 				*pentry = entry->next;	/* remove from list */
-				del_timer(&entry->timer);	/* Paranoia */
-				kfree_s(entry, sizeof(struct arp_table));
+				arp_free_entry(entry);
 			}
 			else
 				pentry = &entry->next;	/* go to next entry */
 		}
 	}
-	arp_cache_stamp++;
-	restore_flags(flags);
 	return NOTIFY_DONE;
 }
 
@@ -338,7 +611,8 @@
 
 void arp_send(int type, int ptype, u32 dest_ip, 
 	      struct device *dev, u32 src_ip, 
-	      unsigned char *dest_hw, unsigned char *src_hw)
+	      unsigned char *dest_hw, unsigned char *src_hw,
+	      unsigned char *target_hw)
 {
 	struct sk_buff *skb;
 	struct arphdr *arp;
@@ -348,7 +622,7 @@
 	 *	No arp on this interface.
 	 */
 	
-	if(dev->flags&IFF_NOARP)
+	if (dev->flags&IFF_NOARP)
 		return;
 
 	/*
@@ -380,7 +654,7 @@
 #ifdef CONFIG_NETROM
 	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
 #else
-	arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP);
+	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
 #endif
 #else
 	arp->ar_pro = htons(ETH_P_IP);
@@ -395,8 +669,8 @@
 	arp_ptr+=dev->addr_len;
 	memcpy(arp_ptr, &src_ip,4);
 	arp_ptr+=4;
-	if (dest_hw != NULL)
-		memcpy(arp_ptr, dest_hw, dev->addr_len);
+	if (target_hw != NULL)
+		memcpy(arp_ptr, target_hw, dev->addr_len);
 	else
 		memset(arp_ptr, 0, dev->addr_len);
 	arp_ptr+=dev->addr_len;
@@ -405,124 +679,35 @@
 	dev_queue_xmit(skb, dev, 0);
 }
 
-
 /*
- *	This function is called, if an entry is not resolved in ARP_RES_TIME.
- *	Either resend a request, or give it up and free the entry.
+ *	This will try to retransmit everything on the queue.
  */
 
-static void arp_expire_request (unsigned long arg)
+static void arp_send_q(struct arp_table *entry)
 {
-	struct arp_table *entry = (struct arp_table *) arg;
-	struct arp_table **pentry;
-	unsigned long hash;
-	unsigned long flags;
+	struct sk_buff *skb;
 
-	save_flags(flags);
-	cli();
+	unsigned long flags;
 
 	/*
-	 *	Since all timeouts are handled with interrupts enabled, there is a
-	 *	small chance, that this entry has just been resolved by an incoming
-	 *	packet. This is the only race condition, but it is handled...
+	 *	Empty the entire queue, building its data up ready to send
 	 */
 	
-	if (entry->flags & ATF_COM)
+	if(!(entry->flags&ATF_COM))
 	{
-		restore_flags(flags);
+		printk("arp_send_q: incomplete entry for %s\n",
+				in_ntoa(entry->ip));
+		/* Can't flush the skb, because RFC1122 says to hang on to */
+		/* at least one from any unresolved entry.  --MS */
+		/* Whats happened is that someone has 'unresolved' the entry
+		   as we got to use it - this 'can't happen' -- AC */
 		return;
 	}
 
-	if (--entry->retries > 0)
-	{
-		u32 ip = entry->ip;
-		struct device *dev = entry->dev;
-
-		/* Set new timer. */
-		del_timer(&entry->timer);
-		entry->timer.expires = jiffies + ARP_RES_TIME;
-		add_timer(&entry->timer);
-		restore_flags(flags);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, 
-			 NULL, dev->dev_addr);
-		return;
-	}
-
-	/*
-	 *	Arp request timed out. Delete entry and all waiting packets.
-	 *	If we give each entry a pointer to itself, we don't have to
-	 *	loop through everything again. Maybe hash is good enough, but
-	 *	I will look at it later.
-	 */
-
-	hash = HASH(entry->ip);
-
-	/* proxy entries shouldn't really time out so this is really
-	   only here for completeness
-	*/
-
-	/* RFC1122: They *can* be timed out, according to 2.3.2.1. */
-	/* They recommend a minute. -- MS */
-	/* The world doesn't work this way -- AC */
-
-	if (entry->flags & ATF_PUBL)
-	  pentry = &arp_tables[PROXY_HASH];
-	else
-	  pentry = &arp_tables[hash];
-	while (*pentry != NULL)
-	{
-		if (*pentry == entry)
-		{
-			*pentry = entry->next;	/* delete from linked list */
-			del_timer(&entry->timer);
-			restore_flags(flags);
-			arp_release_entry(entry);
-			arp_cache_stamp++;
-			return;
-		}
-		pentry = &(*pentry)->next;
-	}
-	restore_flags(flags);
-	printk("Possible ARP queue corruption.\n");
-	/*
-	 *	We should never arrive here.
-	 */
-
-	/* Should we perhaps flush the ARP table (except the ones we're */
-	/* publishing, if we can trust the queue that much) at this */
-	/* point? -- MS */
-}
-
-
-/*
- *	This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
-{
-	struct sk_buff *skb;
-
-	unsigned long flags;
-
-	/*
-	 *	Empty the entire queue, building its data up ready to send
-	 */
-	
-	if(!(entry->flags&ATF_COM))
-	{
-		printk("arp_send_q: incomplete entry for %s\n",
-				in_ntoa(entry->ip));
-		/* Can't flush the skb, because RFC1122 says to hang on to */
-		/* at least one from any unresolved entry.  --MS */
-		/* Whats happened is that someone has 'unresolved' the entry
-		   as we got to use it - this 'can't happen' -- AC */
-		return;
-	}
-
-	save_flags(flags);
-	
-	cli();
-	while((skb = skb_dequeue(&entry->skb)) != NULL)
+	save_flags(flags);
+	
+	cli();
+	while((skb = skb_dequeue(&entry->skb)) != NULL)
 	{
 		IS_SKB(skb);
 		skb_device_lock(skb);
@@ -544,50 +729,29 @@
  *	Delete an ARP mapping entry in the cache.
  */
 
-void arp_destroy(u32 ip_addr, int force)
+static void arp_destroy(struct arp_table * entry)
 {
-        int checked_proxies = 0;
-	struct arp_table *entry;
+	struct arp_table *entry1;
 	struct arp_table **pentry;
-	unsigned long hash = HASH(ip_addr);
 
-ugly:
-	cli();
-	pentry = &arp_tables[hash];
-	if (! *pentry) /* also check proxy entries */
-	  pentry = &arp_tables[PROXY_HASH];
+	if (entry->flags & ATF_PUBL)
+		pentry = &arp_proxy_list;
+	else
+		pentry = &arp_tables[HASH(entry->ip)];
 
-	while ((entry = *pentry) != NULL)
+	while ((entry1 = *pentry) != NULL)
 	{
-		if (entry->ip == ip_addr)
+		if (entry1 == entry)
 		{
-			if ((entry->flags & ATF_PERM) && !force) {
-				sti();
-				return;
-			}
-			*pentry = entry->next;
+			*pentry = entry1->next;
 			del_timer(&entry->timer);
-			sti();
-			arp_release_entry(entry);
-			/* this would have to be cleaned up */
-			goto ugly;
-			/* perhaps like this ?
-			cli();
-			entry = *pentry;
-			*/
+			arp_free_entry(entry);
+			return;
 		}
-		pentry = &entry->next;
-		if (!checked_proxies && ! *pentry)
-		  { /* ugly. we have to make sure we check proxy
-		       entries as well */
-		    checked_proxies = 1;
-		    pentry = &arp_tables[PROXY_HASH];
-		  }
+		pentry = &entry1->next;
 	}
-	sti();
 }
 
-
 /*
  *	Receive an arp request by the device layer. Maybe I rewrite it, to
  *	use the incoming packet for the reply. The time for the current
@@ -604,7 +768,6 @@
 	unsigned char *arp_ptr= (unsigned char *)(arp+1);
 	struct arp_table *entry;
 	struct arp_table *proxy_entry;
-	int hlen,htype;
 	unsigned long hash;
 	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
 	unsigned char *sha,*tha;
@@ -636,7 +799,7 @@
  */
 /* Again, should this be an error/printk? -- MS */
 
-  	switch(dev->type)
+  	switch (dev->type)
   	{
 #ifdef CONFIG_AX25
 		case ARPHRD_AX25:
@@ -683,16 +846,13 @@
  *	Extract fields
  */
 
-	hlen  = dev->addr_len;
-	htype = dev->type;
-
 	sha=arp_ptr;
-	arp_ptr+=hlen;
-	memcpy(&sip,arp_ptr,4);
-	arp_ptr+=4;
+	arp_ptr += dev->addr_len;
+	memcpy(&sip, arp_ptr, 4);
+	arp_ptr += 4;
 	tha=arp_ptr;
-	arp_ptr+=hlen;
-	memcpy(&tip,arp_ptr,4);
+	arp_ptr += dev->addr_len;
+	memcpy(&tip, arp_ptr, 4);
   
 /* 
  *	Check for bad requests for 127.x.x.x and requests for multicast
@@ -721,47 +881,48 @@
  *  cache.
  */
 
-	if(arp->ar_op == htons(ARPOP_REQUEST))
+	if (arp->ar_op == htons(ARPOP_REQUEST))
 	{ 
 /*
  * Only reply for the real device address or when it's in our proxy tables
  */
-		if(tip!=dev->pa_addr)
+		if (tip != dev->pa_addr)
 		{
 /*
  * 	To get in here, it is a request for someone else.  We need to
  * 	check if that someone else is one of our proxies.  If it isn't,
  * 	we can toss it.
  */
-			cli();
-			for(proxy_entry=arp_tables[PROXY_HASH];
-			    proxy_entry;
-			    proxy_entry = proxy_entry->next)
+			arp_fast_lock();
+
+			for (proxy_entry=arp_proxy_list;
+			     proxy_entry;
+			     proxy_entry = proxy_entry->next)
 			{
-			  /* we will respond to a proxy arp request
-			     if the masked arp table ip matches the masked
-			     tip. This allows a single proxy arp table
-			     entry to be used on a gateway machine to handle
-			     all requests for a whole network, rather than
-			     having to use a huge number of proxy arp entries
-			     and having to keep them uptodate.
-			     */
-			  if (proxy_entry->dev != dev && proxy_entry->htype == htype &&
-			      !((proxy_entry->ip^tip)&proxy_entry->mask))
-			    break;
+				/* we will respond to a proxy arp request
+				   if the masked arp table ip matches the masked
+				   tip. This allows a single proxy arp table
+				   entry to be used on a gateway machine to handle
+				   all requests for a whole network, rather than
+				   having to use a huge number of proxy arp entries
+				   and having to keep them uptodate.
+				   */
+				if (proxy_entry->dev == dev &&
+				    !((proxy_entry->ip^tip)&proxy_entry->mask))
+					break;
 
 			}
 			if (proxy_entry)
 			{
-				memcpy(ha, proxy_entry->ha, hlen);
-				sti();
-				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha);
+				memcpy(ha, proxy_entry->ha, dev->addr_len);
+				arp_unlock();
+				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha);
 				kfree_skb(skb, FREE_READ);
 				return 0;
 			}
 			else
 			{
-				sti();
+				arp_unlock();
 				kfree_skb(skb, FREE_READ);
 				return 0;
 			}
@@ -771,7 +932,7 @@
 /*
  * 	To get here, it must be an arp request for us.  We need to reply.
  */
-			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr);
+			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
 		}
 	}
 /*
@@ -791,21 +952,22 @@
  * there.
  */
 
+	arp_fast_lock();
+
 	hash = HASH(sip);
-	cli();
-	for(entry=arp_tables[hash];entry;entry=entry->next)
-		if(entry->ip==sip && entry->htype==htype)
+
+	for (entry=arp_tables[hash]; entry; entry=entry->next)
+		if (entry->ip == sip && entry->dev == dev)
 			break;
 
-	if(entry)
+	if (entry)
 	{
 /*
  *	Entry found; update it only if it is not a permanent entry.
  */
 		if (!(entry->flags & ATF_PERM)) {
-			memcpy(entry->ha, sha, hlen);
-			entry->hlen = hlen;
-			entry->last_used = jiffies;
+			memcpy(entry->ha, sha, dev->addr_len);
+			entry->last_updated = jiffies;
 		}
 		if (!(entry->flags & ATF_COM))
 		{
@@ -815,17 +977,13 @@
  */
 			del_timer(&entry->timer);
 			entry->flags |= ATF_COM;
-			sti();
+			arp_update_hhs(entry);
 /* 
  *	Send out waiting packets. We might have problems, if someone is 
  *	manually removing entries right now -- entry might become invalid 
  *	underneath us.
  */
-			arp_send_q(entry, sha);
-		}
-		else
-		{
-			sti();
+			arp_send_q(entry);
 		}
 	}
 	else
@@ -836,94 +994,121 @@
 		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
 		if(entry == NULL)
 		{
-			sti();
+			arp_unlock();
 			printk("ARP: no memory for new arp entry\n");
-
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
 
                 entry->mask = DEF_ARP_NETMASK;
 		entry->ip = sip;
-		entry->hlen = hlen;
-		entry->htype = htype;
 		entry->flags = ATF_COM;
+		entry->hh    = NULL;
 		init_timer(&entry->timer);
-		memcpy(entry->ha, sha, hlen);
-		entry->last_used = jiffies;
+		entry->timer.function = arp_expire_request;
+		entry->timer.data = (unsigned long)entry;
+		memcpy(entry->ha, sha, dev->addr_len);
+		entry->last_updated = entry->last_used = jiffies;
 		entry->dev = skb->dev;
 		skb_queue_head_init(&entry->skb);
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
-		sti();
+		if (arp_lock == 1)
+		{
+			entry->next = arp_tables[hash];
+			arp_tables[hash] = entry;
+		}
+		else
+		{
+#if RT_CACHE_DEBUG >= 1
+			printk("arp_rcv: %08x backlogged\n", entry->ip);
+#endif
+			arp_enqueue(&arp_backlog, entry);
+			arp_bh_mask |= ARP_BH_BACKLOG;
+		}
 	}
 
 /*
  *	Replies have been sent, and entries have been added.  All done.
  */
 	kfree_skb(skb, FREE_READ);
+	arp_unlock();
 	return 0;
 }
 
-
 /*
- *	Find an arp mapping in the cache. If not found, return false.
+ * Lookup ARP entry by (addr, dev) pair.
+ * Flags: ATF_PUBL - search for proxy entries
+ *	  ATF_NETMASK - search for proxy network entry.
+ * NOTE:  should be called with locked ARP tables.
  */
 
-int arp_query(unsigned char *haddr, u32 paddr, unsigned short type)
+static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev)
 {
 	struct arp_table *entry;
-	unsigned long hash = HASH(paddr);
 
-	/*
-	 *	Find an entry
-	 */
-	cli();
-
-	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
-		if (entry->ip == paddr && entry->htype == type)
-			break;
+	if (!(flags & ATF_PUBL))
+	{
+		for (entry = arp_tables[HASH(paddr)];
+		     entry != NULL; entry = entry->next)
+			if (entry->ip == paddr && entry->dev == dev)
+				break;
+		return entry;
+	}
 
-	if (entry != NULL) {
-		/*
-		 *	Update the record
-		 */
-		
-		entry->last_used = jiffies;
-		memcpy(haddr, entry->ha, entry->hlen);
-		sti();
-		return 1;
+	if (!(flags & ATF_NETMASK))
+	{
+		for (entry = arp_proxy_list;
+		     entry != NULL; entry = entry->next)
+			if (entry->ip == paddr && entry->dev == dev)
+				break;
+		return entry;
 	}
 
-	sti();
-	return 0;
+	for (entry=arp_proxy_list; entry != NULL; entry = entry->next)
+		if (!((entry->ip^paddr)&entry->mask) && entry->dev == dev)
+			break;
+	return entry;
 }
 
 /*
- *	Find an arp mapping in the cache. If not found, post a request.
+ *	Find an arp mapping in the cache. If not found, return false.
  */
 
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
-	   u32 saddr, struct sk_buff *skb)
+int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
 {
 	struct arp_table *entry;
-	unsigned long hash;
-#ifdef CONFIG_IP_MULTICAST
-	u32 taddr;
-#endif	
 
-	switch (ip_chk_addr(paddr))
+	arp_fast_lock();
+
+	entry = arp_lookup(paddr, 0, dev);
+
+	if (entry != NULL)
+	{
+		entry->last_used = jiffies;
+		if (entry->flags & ATF_COM)
+		{
+			memcpy(haddr, entry->ha, dev->addr_len);
+			arp_unlock();
+			return 1;
+		}
+	}
+	arp_unlock();
+	return 0;
+}
+
+
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, __u32 paddr, struct device * dev)
+{
+	switch (addr_hint)
 	{
 		case IS_MYADDR:
 			printk("ARP: arp called for own IP address\n");
 			memcpy(haddr, dev->dev_addr, dev->addr_len);
-			skb->arp = 1;
-			return 0;
+			return 1;
 #ifdef CONFIG_IP_MULTICAST
 		case IS_MULTICAST:
 			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
 			{
-				/* What exactly does this do? -- MS */
+				u32 taddr;
 				haddr[0]=0x01;
 				haddr[1]=0x00;
 				haddr[2]=0x5e;
@@ -933,7 +1118,7 @@
 				haddr[4]=taddr&0xff;
 				taddr=taddr>>8;
 				haddr[3]=taddr&0x7f;
-				return 0;
+				return 1;
 			}
 		/*
 		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
@@ -942,17 +1127,35 @@
 		
 		case IS_BROADCAST:
 			memcpy(haddr, dev->broadcast, dev->addr_len);
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ *	Find an arp mapping in the cache. If not found, post a request.
+ */
+
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+	     u32 saddr, struct sk_buff *skb)
+{
+	struct arp_table *entry;
+	unsigned long hash;
+
+	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
+	{
+		if (skb)
 			skb->arp = 1;
-			return 0;
+		return 0;
 	}
 
 	hash = HASH(paddr);
-	cli();
+	arp_fast_lock();
 
 	/*
 	 *	Find an entry
 	 */
-	entry = arp_lookup(paddr, PROXY_NONE, dev->type);
+	entry = arp_lookup(paddr, 0, dev);
 
 	if (entry != NULL) 	/* It exists */
 	{
@@ -965,10 +1168,30 @@
 			
 			if (skb != NULL)
 			{
-				skb_queue_tail(&entry->skb, skb);
-				skb_device_unlock(skb);
+				if (entry->last_updated)
+				{
+					skb_queue_tail(&entry->skb, skb);
+					skb_device_unlock(skb);
+				}
+				/*
+				 * If last_updated==0 host is dead, so
+				 * drop skb's and set socket error.
+				 */
+				else
+				{
+					/*
+					 * FIXME: ICMP HOST UNREACHABLE should be
+					 *	  sent in this situation. --ANK
+					 */
+					if (skb->sk)
+					{
+						skb->sk->err = EHOSTDOWN;
+						skb->sk->error_report(skb->sk);
+					}
+					dev_kfree_skb(skb, FREE_WRITE);
+				}
 			}
-			sti();
+			arp_unlock();
 			return 1;
 		}
 
@@ -980,7 +1203,7 @@
 		memcpy(haddr, entry->ha, dev->addr_len);
 		if (skb)
 			skb->arp = 1;
-		sti();
+		arp_unlock();
 		return 0;
 	}
 
@@ -992,42 +1215,49 @@
 					GFP_ATOMIC);
 	if (entry != NULL)
 	{
-		entry->next = arp_tables[hash];
-		entry->last_used = jiffies;
+		entry->last_updated = entry->last_used = jiffies;
 		entry->flags = 0;
 		entry->ip = paddr;
 		entry->mask = DEF_ARP_NETMASK;
 		memset(entry->ha, 0, dev->addr_len);
-		entry->hlen = dev->addr_len;
-		entry->htype = dev->type;
 		entry->dev = dev;
+		entry->hh    = NULL;
 		init_timer(&entry->timer);
 		entry->timer.function = arp_expire_request;
 		entry->timer.data = (unsigned long)entry;
 		entry->timer.expires = jiffies + ARP_RES_TIME;
-		arp_tables[hash] = entry;
-		add_timer(&entry->timer);
-		entry->retries = ARP_MAX_TRIES;
 		skb_queue_head_init(&entry->skb);
 		if (skb != NULL)
 		{
 			skb_queue_tail(&entry->skb, skb);
 			skb_device_unlock(skb);
 		}
+		if (arp_lock == 1)
+		{
+			entry->next = arp_tables[hash];
+			arp_tables[hash] = entry;
+			add_timer(&entry->timer);
+			entry->retries = ARP_MAX_TRIES;
+		}
+		else
+		{
+#if RT_CACHE_DEBUG >= 1
+			printk("arp_find: %08x backlogged\n", entry->ip);
+#endif
+			arp_enqueue(&arp_backlog, entry);
+			arp_bh_mask |= ARP_BH_BACKLOG;
+		}
 	}
-	else
-	{
-		if (skb != NULL && skb->free)
-			kfree_skb(skb, FREE_WRITE);
-  	}
-	sti();
+	else if (skb != NULL)
+		dev_kfree_skb(skb, FREE_WRITE);
+	arp_unlock();
 
 	/*
 	 *	If we didn't find an entry, we will try to send an ARP packet.
 	 */
 	
 	arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, 
-		 dev->dev_addr);
+		 dev->dev_addr, NULL);
 
 	return 1;
 }
@@ -1042,7 +1272,6 @@
 int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	int len=0;
-	off_t begin=0;
 	off_t pos=0;
 	int size;
 	struct arp_table *entry;
@@ -1050,12 +1279,13 @@
 	int i,j,k;
 	const char hexbuf[] =  "0123456789ABCDEF";
 
-	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask\n");
+	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
 
 	pos+=size;
 	len+=size;
-	  
-	cli();
+
+	arp_fast_lock();
+
 	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
 	{
 		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
@@ -1065,17 +1295,17 @@
  */
 #ifdef CONFIG_AX25
 #ifdef CONFIG_NETROM
-			if (entry->htype == ARPHRD_AX25 || entry->htype == ARPHRD_NETROM)
+			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
 #else
-			if(entry->htype==ARPHRD_AX25)
+			if(entry->dev->type==ARPHRD_AX25)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
 #endif
 #endif
 
-			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->hlen;j++)
+			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
 			{
 				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
 				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
@@ -1089,200 +1319,330 @@
 			size = sprintf(buffer+len,
 				"%-17s0x%-10x0x%-10x%s",
 				in_ntoa(entry->ip),
-				(unsigned int)entry->htype,
+				(unsigned int)entry->dev->type,
 				entry->flags,
 				hbuffer);
+#if RT_CACHE_DEBUG < 2
 			size += sprintf(buffer+len+size,
-				 "     %-17s\n",
-				  entry->mask==DEF_ARP_NETMASK?
-				   "*":in_ntoa(entry->mask));
+				 "     %-17s %s\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name);
+#else
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\t%ld\t%1d\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name, 
+				 entry->hh ? entry->hh->hh_refcnt : -1,
+				 entry->hh ? entry->hh->hh_uptodate : 0);
+#endif
 	
-			len+=size;
-			pos=begin+len;
+			len += size;
+			pos += size;
 		  
-			if(pos<offset)
-			{
+			if (pos <= offset)
 				len=0;
-				begin=pos;
-			}
-			if(pos>offset+length)
+			if (pos >= offset+length)
 				break;
 		}
 	}
-	sti();
+	arp_unlock();
   
-	*start=buffer+(offset-begin);	/* Start of wanted data */
-	len-=(offset-begin);		/* Start slop */
-	if(len>length)
-		len=length;			/* Ending slop */
+	*start = buffer+len-(pos-offset);	/* Start of wanted data */
+	len = pos-offset;			/* Start slop */
+	if (len>length)
+		len = length;			/* Ending slop */
 	return len;
 }
 
 
-/*
- *	This will find an entry in the ARP table by looking at the IP address.
- *	If proxy is PROXY_EXACT then only exact IP matches will be allowed
- *	for proxy entries, otherwise the netmask will be used
- */
 
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type)
+int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
 {
 	struct arp_table *entry;
-	unsigned long hash = HASH(paddr);
-	
-	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
-		if (entry->ip == paddr && entry->htype == type)
-			break;
+	struct hh_cache *hh = *hhp;
+	int addr_hint;
+	unsigned long flags;
 
-	/* it's possibly a proxy entry (with a netmask) */
-	if (!entry && proxy != PROXY_NONE)
-	for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next)
-	  if ((proxy==PROXY_EXACT) ? (entry->ip==paddr)
-				   : !((entry->ip^paddr)&entry->mask)) 
-	    break;	  
+	if (hh)
+		return 1;
 
-	return entry;
-}
+	if ((addr_hint = ip_chk_addr(paddr)) != 0)
+	{
+		unsigned char haddr[MAX_ADDR_LEN];
+		if (hh)
+			return 1;
+		hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+		if (!hh)
+			return 1;
+		arp_set_predefined(addr_hint, haddr, paddr, dev);
+		hh->hh_uptodate = 0;
+		hh->hh_refcnt = 1;
+		hh->hh_arp = NULL;
+		hh->hh_next = NULL;
+		hh->hh_type = htype;
+		*hhp = hh;
+		dev->header_cache_update(hh, dev, haddr);
+		return 0;
+	}
 
+	save_flags(flags);
 
-int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev)
-{	
-	/* 
-	 *	We need the broadcast/multicast awareness here and the find routine split up.
-	 */
-	struct arp_table *entry;
-#ifdef CONFIG_IP_MULTICAST
-	u32 taddr;
-#endif	
+	arp_fast_lock();
+
+	entry = arp_lookup(paddr, 0, dev);
 
-	switch (ip_chk_addr(daddr))
+	if (entry)
 	{
-		case IS_MYADDR:
-			printk("ARP: arp called for own IP address\n");
-			memcpy(dp, dev->dev_addr, dev->addr_len);
+		cli();
+		for (hh = entry->hh; hh; hh=hh->hh_next)
+			if (hh->hh_type == htype)
+				break;
+		if (hh)
+		{
+			hh->hh_refcnt++;
+			*hhp = hh;
+			restore_flags(flags);
+			arp_unlock();
 			return 1;
-#ifdef CONFIG_IP_MULTICAST
-		case IS_MULTICAST:
-			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+		}
+		restore_flags(flags);
+	}
+
+	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+	if (!hh)
+	{
+		arp_unlock();
+		return 1;
+	}
+
+	hh->hh_uptodate = 0;
+	hh->hh_refcnt = 1;
+	hh->hh_arp = NULL;
+	hh->hh_next = NULL;
+	hh->hh_type = htype;
+
+	if (entry)
+	{
+		dev->header_cache_update(hh, dev, entry->ha);
+		*hhp = hh;
+		cli();
+		hh->hh_arp = (void*)entry;
+		entry->hh = hh;
+		hh->hh_refcnt++;
+		restore_flags(flags);
+		entry->last_used = jiffies;
+		arp_unlock();
+		return 0;
+	}
+
+
+	/*
+	 *	Create a new unresolved entry.
+	 */
+	
+	entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+					GFP_ATOMIC);
+	if (entry == NULL)
+	{
+		kfree_s(hh, sizeof(struct hh_cache));
+		arp_unlock();
+		return 1;
+	}
+
+	entry->last_updated = entry->last_used = jiffies;
+	entry->flags = 0;
+	entry->ip = paddr;
+	entry->mask = DEF_ARP_NETMASK;
+	memset(entry->ha, 0, dev->addr_len);
+	entry->dev = dev;
+	entry->hh = hh;
+	ATOMIC_INCR(&hh->hh_refcnt);
+	init_timer(&entry->timer);
+	entry->timer.function = arp_expire_request;
+	entry->timer.data = (unsigned long)entry;
+	entry->timer.expires = jiffies + ARP_RES_TIME;
+	skb_queue_head_init(&entry->skb);
+
+	if (arp_lock == 1)
+	{
+		unsigned long hash = HASH(paddr);
+		cli();
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
+		hh->hh_arp = (void*)entry;
+		entry->retries = ARP_MAX_TRIES;
+		restore_flags(flags);
+
+		add_timer(&entry->timer);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+	}
+	else
+	{
+#if RT_CACHE_DEBUG >= 1
+		printk("arp_cache_bind: %08x backlogged\n", entry->ip);
+#endif
+		arp_enqueue(&arp_backlog, entry);
+		arp_bh_mask |= ARP_BH_BACKLOG;
+	}
+	*hhp = hh;
+	arp_unlock();
+	return 0;
+}
+
+static void arp_run_bh()
+{
+	unsigned long flags;
+	struct arp_table *entry, *entry1;
+	struct hh_cache *hh;
+	__u32 sip;
+
+	save_flags(flags);
+	cli();
+	if (!arp_lock)
+	{
+		arp_fast_lock();
+
+		while ((entry = arp_dequeue(&arp_backlog)) != NULL)
+		{
+			unsigned long hash;
+			sti();
+			sip = entry->ip;
+			hash = HASH(sip);
+
+			/* It's possible, that an entry with the same pair 
+			 * (addr,type) was already created. Our entry is older,
+			 * so it should be discarded.
+			 */
+			for (entry1=arp_tables[hash]; entry1; entry1=entry1->next)
+				if (entry1->ip==sip && entry1->dev == entry->dev)
+					break;
+
+			if (!entry1)
 			{
-				dp[0]=0x01;
-				dp[1]=0x00;
-				dp[2]=0x5e;
-				taddr=ntohl(daddr);
-				dp[5]=taddr&0xff;
-				taddr=taddr>>8;
-				dp[4]=taddr&0xff;
-				taddr=taddr>>8;
-				dp[3]=taddr&0x7f;
-				return 1;
-			}
-		/*
-		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
-		 */
+				struct device  * dev = entry->dev;
+				cli();
+				entry->next = arp_tables[hash];
+				arp_tables[hash] = entry;
+				for (hh=entry->hh; hh; hh=hh->hh_next)
+					hh->hh_arp = (void*)entry;
+				sti();
+				del_timer(&entry->timer);
+				entry->timer.expires = jiffies + ARP_RES_TIME;
+				add_timer(&entry->timer);
+				entry->retries = ARP_MAX_TRIES;
+				arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+#if RT_CACHE_DEBUG >= 1
+				printk("arp_run_bh: %08x reinstalled\n", sip);
 #endif
-		
-		case IS_BROADCAST:
-			memcpy(dp, dev->broadcast, dev->addr_len);
-			return 1;
-			
-		default:
-			entry=arp_lookup(daddr, PROXY_NONE, dev->type);
-			if(entry)
+			}
+			else
 			{
-				memcpy(dp,entry->ha, ETH_ALEN);
-				return 1;
+				struct sk_buff * skb;
+				struct hh_cache * next;
+
+				/* Discard entry, but preserve its hh's and
+				 * skb's.
+				 */
+				cli();
+				for (hh=entry->hh; hh; hh=next)
+				{
+					next = hh->hh_next;
+					hh->hh_next = entry1->hh;
+					entry1->hh = hh;
+					hh->hh_arp = (void*)entry1;
+				}
+				entry->hh = NULL;
+
+				/* Prune skb list from entry
+				 * and graft it to entry1.
+				 */
+				while ((skb = skb_dequeue(&entry->skb)) != NULL)
+				{
+					skb_device_lock(skb);
+					sti();
+					skb_queue_tail(&entry1->skb, skb);
+					skb_device_unlock(skb);
+					cli();
+				}
+				sti();
+				
+#if RT_CACHE_DEBUG >= 1
+				printk("arp_run_bh: entry %08x was born dead\n", entry->ip);
+#endif
+				arp_free_entry(entry);
+
+				if (entry1->flags & ATF_COM)
+				{
+					arp_update_hhs(entry1);
+					arp_send_q(entry1);
+				}
 			}
+			cli();
+		}
+		arp_bh_mask  &= ~ARP_BH_BACKLOG;
+		arp_unlock();
 	}
-	return 0;
+	restore_flags(flags);
 }
 
+
 /*
  *	Set (create) an ARP cache entry.
  */
 
-static int arp_req_set(struct arpreq *req)
+static int arp_req_set(struct arpreq *r, struct device * dev)
 {
-	struct arpreq r;
 	struct arp_table *entry;
 	struct sockaddr_in *si;
-	int htype, hlen;
 	struct rtable *rt;
+	struct device * dev1;
 	u32 ip;
 
-	memcpy_fromfs(&r, req, sizeof(r));
-
-	/* We only understand about IP addresses... */
-	if (r.arp_pa.sa_family != AF_INET)
-		return -EPFNOSUPPORT;
-
 	/*
 	 * Find out about the hardware type.
 	 * We have to be compatible with BSD UNIX, so we have to
 	 * assume that a "not set" value (i.e. 0) means Ethernet.
+	 *
+	 * ANK: Hey, who wrote it? Do you really mean that BSD considers 
+	 *	ARPHRD_NETROM as ARPHRD_ETHER, or somthing another?
 	 */
 	
-	switch (r.arp_ha.sa_family) {
-		case ARPHRD_ETHER:
-			htype = ARPHRD_ETHER;
-			hlen = ETH_ALEN;
-			break;
-
-		case ARPHRD_ARCNET:
-			htype = ARPHRD_ARCNET;
-			hlen = 1;	/* length of arcnet addresses */
-			break;
-
-#ifdef CONFIG_AX25
-		case ARPHRD_AX25:
-			htype = ARPHRD_AX25;
-			hlen = AX25_ADDR_LEN;
-			break;
-#endif
-#ifdef CONFIG_NETROM
-		case ARPHRD_NETROM:
-			htype = ARPHRD_NETROM;
-			hlen = AX25_ADDR_LEN;
-			break;
-#endif
-		case ARPHRD_IEEE802:
-			htype = ARPHRD_IEEE802;
-			hlen = TR_ALEN;
-			break;
-		default:
-			return -EPFNOSUPPORT;
-	}
-
-	si = (struct sockaddr_in *) &r.arp_pa;
+	si = (struct sockaddr_in *) &r->arp_pa;
 	ip = si->sin_addr.s_addr;
-	if (ip == 0)
-	{
-		printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
-		return -EINVAL;
-	}
 
 	/*
-	 *	Is it reachable directly ?
+	 *	Is it reachable ?
 	 */
 
-	rt = ip_rt_route(ip, NULL, NULL);
-	if (rt == NULL)
+	rt = ip_rt_route(ip, 0);
+	if (!rt)
 		return -ENETUNREACH;
+	dev1 = rt->rt_dev;
+	ip_rt_put(rt);
+
+	if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
+	    (!(r->arp_flags & ATF_PUBL) && dev != dev1))
+		return -EINVAL;
+
+#if RT_CACHE_DEBUG >= 1
+	if (arp_lock)
+		printk("arp_req_set: bug\n");
+#endif
+	arp_fast_lock();
 
 	/*
 	 *	Is there an existing entry for this address?
 	 */
-	
-	cli();
 
 	/*
 	 *	Find the entry
 	 */
-	entry = arp_lookup(ip, PROXY_EXACT, htype);
-	if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL))
+	
+	entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev);
+
+	if (entry)
 	{
-		sti();
-		arp_destroy(ip,1);
-		cli();
+		arp_destroy(entry);
 		entry = NULL;
 	}
 
@@ -1292,77 +1652,83 @@
 	
 	if (entry == NULL)
 	{
-		unsigned long hash = HASH(ip);
-		if (r.arp_flags & ATF_PUBL)
-		  hash = PROXY_HASH;
-
 		entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
 					GFP_ATOMIC);
 		if (entry == NULL)
 		{
-			sti();
+			arp_unlock();
 			return -ENOMEM;
 		}
 		entry->ip = ip;
-		entry->hlen = hlen;
-		entry->htype = htype;
+		entry->hh = NULL;
 		init_timer(&entry->timer);
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
+		entry->timer.function = arp_expire_request;
+		entry->timer.data = (unsigned long)entry;
+
+		if (r->arp_flags & ATF_PUBL)
+		{
+			cli();
+			entry->next = arp_proxy_list;
+			arp_proxy_list = entry;
+			sti();
+		}
+		else
+		{
+			unsigned long hash = HASH(ip);
+			cli();
+			entry->next = arp_tables[hash];
+			arp_tables[hash] = entry;
+			sti();
+		}
 		skb_queue_head_init(&entry->skb);
 	}
 	/*
 	 *	We now have a pointer to an ARP entry.  Update it!
 	 */
 	
-	memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
-	entry->last_used = jiffies;
-	entry->flags = r.arp_flags | ATF_COM;
+	if ((r->arp_flags & ATF_COM) && !r->arp_ha.sa_data[0])
+		memcpy(&entry->ha, dev->dev_addr, dev->addr_len);
+	else
+		memcpy(&entry->ha, &r->arp_ha.sa_data, dev->addr_len);
+	entry->last_updated = entry->last_used = jiffies;
+	entry->flags = r->arp_flags | ATF_COM;
 	if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK))
-	  {
-	    si = (struct sockaddr_in *) &r.arp_netmask;
-	    entry->mask = si->sin_addr.s_addr;
-	  }
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		entry->mask = si->sin_addr.s_addr;
+	}
 	else
-	  entry->mask = DEF_ARP_NETMASK;
-	entry->dev = rt->rt_dev;
-	arp_cache_stamp++;
-	sti();
-
+		entry->mask = DEF_ARP_NETMASK;
+	entry->dev = dev;
+	arp_update_hhs(entry);
+	arp_unlock();
 	return 0;
 }
 
 
+
 /*
  *	Get an ARP cache entry.
  */
 
-static int arp_req_get(struct arpreq *req)
+static int arp_req_get(struct arpreq *r, struct device *dev)
 {
-	struct arpreq r;
 	struct arp_table *entry;
 	struct sockaddr_in *si;
 
-	/*
-	 *	We only understand about IP addresses...
-	 */
-	
-	memcpy_fromfs(&r, req, sizeof(r));
+	si = (struct sockaddr_in *) &r->arp_pa;
 
-	if (r.arp_pa.sa_family != AF_INET)
-		return -EPFNOSUPPORT;
+#if RT_CACHE_DEBUG >= 1
+	if (arp_lock)
+		printk("arp_req_set: bug\n");
+#endif
+	arp_fast_lock();
 
-	/*
-	 *	Is there an existing entry for this address?
-	 */
-	
-	si = (struct sockaddr_in *) &r.arp_pa;
-	cli();
-	entry = arp_lookup(si->sin_addr.s_addr, PROXY_ANY, r.arp_ha.sa_family);
+	entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev);
 
 	if (entry == NULL)
 	{
-		sti();
+		arp_unlock();
 		return -ENXIO;
 	}
 
@@ -1370,19 +1736,54 @@
 	 *	We found it; copy into structure.
 	 */
 	
-	memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
-	r.arp_ha.sa_family = entry->htype;
-	r.arp_flags = entry->flags;
-	sti();
-
-	/*
-	 *	Copy the information back
-	 */
-	
-	memcpy_tofs(req, &r, sizeof(r));
+	memcpy(r->arp_ha.sa_data, &entry->ha, entry->dev->addr_len);
+	r->arp_ha.sa_family = entry->dev->type;
+	r->arp_flags = entry->flags;
+	strncpy(r->arp_dev, entry->dev->name, 16);
+	arp_unlock();
 	return 0;
 }
 
+static int arp_req_delete(struct arpreq *r, struct device * dev)
+{
+	struct arp_table *entry;
+	struct sockaddr_in *si;
+
+	si = (struct sockaddr_in *) &r->arp_pa;
+#if RT_CACHE_DEBUG >= 1
+	if (arp_lock)
+		printk("arp_req_delete: bug\n");
+#endif
+	arp_fast_lock();
+
+	if (!(r->arp_flags & ATF_PUBL))
+	{
+		for (entry = arp_tables[HASH(si->sin_addr.s_addr)];
+		     entry != NULL; entry = entry->next)
+			if (entry->ip == si->sin_addr.s_addr 
+			    && entry->dev == dev)
+			{
+				arp_destroy(entry);
+				arp_unlock();
+				return 0;
+			}
+	}
+	else
+	{
+		for (entry = arp_proxy_list;
+		     entry != NULL; entry = entry->next)
+			if (entry->ip == si->sin_addr.s_addr 
+			    && entry->dev == dev) 
+			{
+				arp_destroy(entry);
+				arp_unlock();
+				return 0;
+			}
+	}
+
+	arp_unlock();
+	return -ENXIO;
+}
 
 /*
  *	Handle an ARP layer I/O control request.
@@ -1390,38 +1791,115 @@
 
 int arp_ioctl(unsigned int cmd, void *arg)
 {
-	struct arpreq r;
-	struct sockaddr_in *si;
 	int err;
+	struct arpreq r;
+
+	struct device * dev = NULL;
 
 	switch(cmd)
 	{
 		case SIOCDARP:
+		case SIOCSARP:
 			if (!suser())
 				return -EPERM;
-			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
-			if(err)
-				return err;
-			memcpy_fromfs(&r, arg, sizeof(r));
-			if (r.arp_pa.sa_family != AF_INET)
-				return -EPFNOSUPPORT;
-			si = (struct sockaddr_in *) &r.arp_pa;
-			arp_destroy(si->sin_addr.s_addr, 1);
-			return 0;
 		case SIOCGARP:
-			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
-			if(err)
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+			if (err)
 				return err;
-			return arp_req_get((struct arpreq *)arg);
-		case SIOCSARP:
+			memcpy_fromfs(&r, arg, sizeof(struct arpreq));
+			break;
+		case OLD_SIOCDARP:
+		case OLD_SIOCSARP:
 			if (!suser())
 				return -EPERM;
-			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
-			if(err)
+		case OLD_SIOCGARP:
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq_old));
+			if (err)
 				return err;
-			return arp_req_set((struct arpreq *)arg);
+			memcpy_fromfs(&r, arg, sizeof(struct arpreq_old));
+			memset(&r.arp_dev, 0, sizeof(r.arp_dev));
+			break;
 		default:
 			return -EINVAL;
+	}
+
+	if (r.arp_pa.sa_family != AF_INET)
+		return -EPFNOSUPPORT;
+	if (((struct sockaddr_in *)&r.arp_pa)->sin_addr.s_addr == 0)
+		return -EINVAL;
+
+	if (r.arp_dev[0])
+	{
+		if ((dev = dev_get(r.arp_dev)) == NULL)
+			return -ENODEV;
+
+		if (!r.arp_ha.sa_family)
+			r.arp_ha.sa_family = dev->type;
+		else if (r.arp_ha.sa_family != dev->type)
+			return -EINVAL;
+	}
+	else
+	{
+		/*
+		 * Device was not specified. Take the first suitable one.
+		 */
+		if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL)
+			return -ENODEV;
+	}
+
+	switch(cmd)
+	{
+		case SIOCDARP:
+		        return arp_req_delete(&r, dev);
+		case SIOCSARP:
+			return arp_req_set(&r, dev);
+		case OLD_SIOCDARP:
+			/* old  SIOCDARP destoyes both
+			 * normal and proxy mappings
+			 */
+			r.arp_flags &= ~ATF_PUBL;
+			err = arp_req_delete(&r, dev);
+			r.arp_flags |= ATF_PUBL;
+			if (!err)
+				arp_req_delete(&r, dev);
+			else
+				err = arp_req_delete(&r, dev);
+			return err;
+		case OLD_SIOCSARP:
+			err = arp_req_set(&r, dev);
+			/* old SIOCSARP works so funny,
+			 * that its behaviour can be emulated
+			 * only approximately 8).
+			 * It should work. --ANK
+			 */
+			if (r.arp_flags & ATF_PUBL)
+			{	
+				r.arp_flags &= ~ATF_PUBL;
+				arp_req_delete(&r, dev);
+			}
+			return err;
+		case SIOCGARP:
+			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+			if (err)
+				return err;
+			err = arp_req_get(&r, dev);
+			if (!err)
+				memcpy_tofs(arg, &r, sizeof(r));
+			return err;
+		case OLD_SIOCGARP:
+			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq_old));
+			if (err)
+				return err;
+			r.arp_flags &= ~ATF_PUBL;
+			err = arp_req_get(&r, dev);
+			if (err < 0)
+			{
+				r.arp_flags |= ATF_PUBL;
+				err = arp_req_get(&r, dev);
+			}
+			if (!err)
+				memcpy_tofs(arg, &r, sizeof(struct arpreq_old));
+			return err;
 	}
 	/*NOTREACHED*/
 	return 0;
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v1.3.41/linux/net/ipv4/devinet.c	Wed Nov  8 07:11:45 1995
+++ linux/net/ipv4/devinet.c	Tue Nov 14 16:03:57 1995
@@ -227,3 +227,16 @@
 	}
 	return NULL;
 }
+
+struct device *dev_getbytype(unsigned short type)
+{
+	struct device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
+			return(dev);
+	}
+	return(NULL);
+}
+
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.41/linux/net/ipv4/icmp.c	Sun Oct 29 11:38:49 1995
+++ linux/net/ipv4/icmp.c	Tue Nov 14 17:06:13 1995
@@ -362,9 +362,51 @@
 			case ICMP_PORT_UNREACH:
 				break;
 			case ICMP_FRAG_NEEDED:
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
 				printk("ICMP: %s: fragmentation needed and DF set.\n",
 								in_ntoa(iph->daddr));
 				break;
+#else
+			{
+				unsigned short old_mtu = ntohs(iph->tot_len);
+				unsigned short new_mtu = ntohs(icmph->un.echo.sequence);
+
+				if (new_mtu < 68 || new_mtu >= old_mtu)
+				{
+					/*
+					 * 	It is either dumb router, which does not
+					 *	understand Path MTU Disc. protocol
+					 *	or broken (f.e. Linux<=1.3.37 8) router.
+					 *	Try to guess...
+					 *	The table is taken from RFC-1191.
+					 */
+					if (old_mtu > 32000)
+						new_mtu = 32000;
+					else if (old_mtu > 17914)
+						new_mtu = 17914;
+					else if (old_mtu > 8166)
+						new_mtu = 8166;
+					else if (old_mtu > 4352)
+						new_mtu = 4352;
+					else if (old_mtu > 2002)
+						new_mtu = 2002;
+					else if (old_mtu > 1492)
+						new_mtu = 1492;
+					else if (old_mtu > 576)
+						new_mtu = 576;
+					else if (old_mtu > 296)
+						new_mtu = 296;
+					else
+						new_mtu = 68;
+				}
+				/*
+				 * Ugly trick to pass MTU to protocol layer.
+				 * Really we should add argument "info" to error handler.
+				 */
+				iph->id = htons(new_mtu);
+				break;
+			}
+#endif
 			case ICMP_SR_FAILED:
 				printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
 				break;
@@ -427,9 +469,6 @@
 
 static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 source, __u32 daddr, int len)
 {
-#ifndef CONFIG_IP_FORWARD
-	struct rtable *rt;
-#endif
 	struct iphdr *iph;
 	unsigned long ip;
 
@@ -472,16 +511,8 @@
 			 *	(not some confused thing sending our
 			 *	address)
 			 */
-			rt = ip_rt_route(ip, NULL, NULL);
-			if (!rt)
-				break;
-			if (rt->rt_gateway != source || 
-				((icmph->un.gateway^dev->pa_addr)&dev->pa_mask) ||
-				ip_chk_addr(icmph->un.gateway))
-				break;
 			printk("ICMP redirect from %s\n", in_ntoa(source));
-			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
-				ip, 0, icmph->un.gateway, dev,0, 0, 0, 0);
+			ip_rt_redirect(source, ip, icmph->un.gateway, dev);
 			break;
 		case ICMP_REDIR_NETTOS:
 		case ICMP_REDIR_HOSTTOS:
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.41/linux/net/ipv4/igmp.c	Wed Sep 13 12:45:34 1995
+++ linux/net/ipv4/igmp.c	Tue Nov 14 16:03:57 1995
@@ -103,7 +103,7 @@
 	if(skb==NULL)
 		return;
 	tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
-				28 , 0, 1);
+				28 , 0, 1, NULL);
 	if(tmp<0)
 	{
 		kfree_skb(skb, FREE_WRITE);
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.41/linux/net/ipv4/ip_forward.c	Sun Oct 29 11:38:49 1995
+++ linux/net/ipv4/ip_forward.c	Tue Nov 14 16:03:58 1995
@@ -90,6 +90,7 @@
 	unsigned char *ptr;	/* Data pointer */
 	unsigned long raddr;	/* Router IP address */
 	struct   options * opt	= (struct options*)skb->proto_priv;
+	struct hh_cache *hh = NULL;
 	int encap = 0;		/* Encap length */
 #ifdef CONFIG_FIREWALL
 	int fw_res = 0;		/* Forwarding result */	
@@ -159,7 +160,8 @@
 		 * and give it to the IP sender for further processing.
 		 */
 
-		rt = ip_rt_route(target_addr, NULL, NULL);
+		rt = ip_rt_route(target_addr, 0);
+
 		if (rt == NULL)
 		{
 			/*
@@ -181,31 +183,22 @@
 
 		raddr = rt->rt_gateway;
 	
-		if (raddr != 0)
-		{
+		if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
 			/*
 			 *	Strict routing permits no gatewaying
 			 */
 	
-		        if (opt->is_strictroute)
-			{
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
-				return -1;
-			}
-		
-			/*
-			 *	There is a gateway so find the correct route for it.
-			 *	Gateways cannot in turn be gatewayed.
-			 */
+			ip_rt_put(rt);
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+			return -1;
 		}
-		else
-			raddr = target_addr;
 
 		/*
 		 *	Having picked a route we can now send the frame out.
 		 */
 
 		dev2 = rt->rt_dev;
+		hh = rt->rt_hh;
 		/*
 		 *	In IP you never have to forward a frame on the interface that it 
 		 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
@@ -227,6 +220,7 @@
 		raddr=skb->raddr;
 		if(is_frag&16)		/* VIFF_TUNNEL mode */
 			encap=20;
+		rt=NULL;
 	}
 #endif	
 	
@@ -250,7 +244,8 @@
 
 		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
 		  ip_statistics.IpFragFails++;
-		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev);
+		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
+		  ip_rt_put(rt);
 		  return -1;
 		}
 
@@ -271,6 +266,7 @@
 			if (skb2 == NULL)
 			{
 				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
+				ip_rt_put(rt);
 				return -1;
 			}
 		
@@ -286,7 +282,7 @@
 			}
 			else
 #endif			
-		 		ip_send(skb2,raddr,skb->len,dev2,dev2->pa_addr);
+		 		ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr);
 
 			/*
 			 *	We have to copy the bytes over as the new header wouldn't fit
@@ -320,7 +316,18 @@
 #endif
 				skb->arp=1;
 				skb->raddr=raddr;
-				if(dev2->hard_header)
+				if (hh)
+				{
+					memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len);
+					if (!hh->hh_uptodate)
+					{
+#if RT_CACHE_DEBUG >= 2
+						printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway);
+#endif						
+						skb->arp = 0;
+					}
+				}
+				else if (dev2->hard_header)
 				{
 					if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
 						skb->arp=0;
@@ -418,7 +425,11 @@
 		}
 	}
 	else
+	{
+	        ip_rt_put(rt);
 		return -1;
+	}
+	ip_rt_put(rt);
 	
 	/*
 	 *	Tell the caller if their buffer is free.
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.41/linux/net/ipv4/ip_fw.c	Mon Oct 23 18:02:23 1995
+++ linux/net/ipv4/ip_fw.c	Tue Nov 14 16:03:58 1995
@@ -196,8 +196,8 @@
 	__u16			src_port=0, dst_port=0, icmp_type=0;
 	unsigned short		f_prt=0, prt;
 	char			notcpsyn=1, notcpack=1, match;
-	unsigned short		f_flag;
 	unsigned short		offset;
+	int			answer, priority;
 
 	/*
 	 *	If the chain is empty follow policy. The BSD one
@@ -465,24 +465,39 @@
 			break;
 	} /* Loop */
 	
-	if(opt == 1)
-		return 0;
-
+	answer = FW_BLOCK;
+	
 	/*
 	 * We rely on policy defined in the rejecting entry or, if no match
 	 * was found, we rely on the general policy variable for this type
 	 * of firewall.
 	 */
 
-	if(f!=NULL)	/* A match was found */
-		f_flag=f->fw_flg;
+	if(f!=NULL) 
+	{
+		policy=f->fw_flg;
+		priority=f->fw_priority;
+	}
 	else
-		f_flag=policy;
-	if(f_flag&IP_FW_F_ACCEPT)
-		return ((f_flag&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT);
-	if(f_flag&IP_FW_F_ICMPRPL)
-		return FW_REJECT;
-	return FW_BLOCK;
+		priority=0xFF00;
+
+	if(opt != 1) 
+	{
+		if(policy&IP_FW_F_ACCEPT)
+			answer=(policy&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT;
+		else
+			if(policy&IP_FW_F_ICMPRPL)
+				answer = FW_REJECT;
+	}
+
+	if (answer == 0) { /* Adjust priority and recompute checksum */
+		__u8 old_tos = ip->tos;
+		ip->tos = (old_tos & (priority>>8)) ^ priority;
+		if (ip->tos != old_tos)
+		 	ip_send_check(ip);
+	}
+
+	return answer;
 }
 
 #ifdef CONFIG_IP_MASQUERADE
@@ -1022,7 +1037,7 @@
 
 /* Volatiles to keep some of the compiler versions amused */
 
-static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl)
+static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
 {
 	struct ip_fw *ftmp;
 	struct ip_fw *chtmp=NULL;
@@ -1046,8 +1061,11 @@
 		return( ENOMEM );
 	}
 
-	memcpy(ftmp, frwl, sizeof( struct ip_fw ) );
+	memcpy(ftmp, frwl, len);
+	if (len == sizeof (struct ip_fw_old))
+		ftmp->fw_priority = 0xFF00; /* and_mask, xor_mask */
 
+	ftmp->fw_priority = (ftmp->fw_priority & 0xFFFC) | 0x0300;
 	ftmp->fw_pcnt=0L;
 	ftmp->fw_bcnt=0L;
 
@@ -1286,7 +1304,7 @@
 struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
 {
 
-	if ( len != sizeof(struct ip_fw) )
+	if ( len != sizeof(struct ip_fw) && len != sizeof(struct ip_fw_old))
 	{
 #ifdef DEBUG_CONFIG_IP_FIREWALL
 		printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
@@ -1370,7 +1388,7 @@
 		switch (stage) 
 		{
 			case IP_ACCT_ADD:
-				return( add_to_chain(&ip_acct_chain,frwl));
+				return( add_to_chain(&ip_acct_chain,frwl,len));
 		    	case IP_ACCT_DEL:
 				return( del_from_chain(&ip_acct_chain,frwl));
 			default:
@@ -1489,9 +1507,9 @@
 		switch (stage) 
 		{
 			case IP_FW_ADD_BLK:
-				return(add_to_chain(&ip_fw_blk_chain,frwl));
+				return(add_to_chain(&ip_fw_blk_chain,frwl,len));
 			case IP_FW_ADD_FWD:
-				return(add_to_chain(&ip_fw_fwd_chain,frwl));
+				return(add_to_chain(&ip_fw_fwd_chain,frwl,len));
 			case IP_FW_DEL_BLK:
 				return(del_from_chain(&ip_fw_blk_chain,frwl));
 			case IP_FW_DEL_FWD: 
@@ -1565,6 +1583,7 @@
 			i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
 		for (p = 0; p < IP_FW_MAX_PORTS; p++)
 			len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
+		len+=sprintf(buffer+len, " M%04X", i->fw_priority);
 		buffer[len++]='\n';
 		buffer[len]='\0';
 		pos=begin+len;
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v1.3.41/linux/net/ipv4/ip_options.c	Wed Nov  8 07:11:45 1995
+++ linux/net/ipv4/ip_options.c	Tue Nov 14 16:03:58 1995
@@ -118,14 +118,14 @@
 		memcpy(dptr, sptr+sopt->ts, optlen);
 		if (soffset <= optlen) 
 		{
-			if (dopt->ts_needaddr) 
+			if (sopt->ts_needaddr) 
 			{
 				if (soffset + 3 > optlen)
 					return -EINVAL;
 				dopt->ts_needaddr = 1;
 				soffset += 4;
 			}
-			if (dopt->ts_needtime) 
+			if (sopt->ts_needtime) 
 			{
 				if (soffset + 3 > optlen)
 					return -EINVAL;
@@ -376,10 +376,8 @@
 				      case IPOPT_TS_TSONLY:
 					opt->ts = optptr - iph;
 					if (skb) 
-					{
 						timeptr = (__u32*)&optptr[ts->ptr-1];
-						opt->is_changed = 1;
-					}
+					opt->ts_needtime = 1;
 					ts->ptr += 4;
 					break;
 				      case IPOPT_TS_TSANDADDR:
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.41/linux/net/ipv4/ip_output.c	Wed Nov  8 07:11:46 1995
+++ linux/net/ipv4/ip_output.c	Tue Nov 14 16:03:58 1995
@@ -86,7 +86,7 @@
 	/*
 	 *	Put a MAC header on the packet
 	 */
-	ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
+	ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
 	/*
 	 *	Add the rest of the data space.	
 	 */
@@ -110,7 +110,7 @@
  *	Take an skb, and fill in the MAC header.
  */
 
-int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
 {
 	int mac = 0;
 
@@ -123,6 +123,18 @@
 		 *  	(rebuild header will sort this out)
 		 */
 		skb_reserve(skb,(dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
+		if (rt && dev == rt->rt_dev && rt->rt_hh)
+		{
+			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+			if (rt->rt_hh->hh_uptodate)
+				return dev->hard_header_len;
+#if RT_CACHE_DEBUG >= 2
+			printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
+#endif
+			skb->arp = 0;
+			skb->raddr = daddr;
+			return -dev->hard_header_len;
+		}
 		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
 		if (mac < 0)
 		{
@@ -134,7 +146,7 @@
 	return mac;
 }
 
-static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
 {
 	int mac = 0;
 
@@ -143,6 +155,18 @@
 	if (dev->hard_header)
 	{
 		skb_reserve(skb,MAX_HEADER);
+		if (rt && dev == rt->rt_dev && rt->rt_hh)
+		{
+			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+			if (rt->rt_hh->hh_uptodate)
+				return dev->hard_header_len;
+#if RT_CACHE_DEBUG >= 2
+			printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
+#endif
+			skb->arp = 0;
+			skb->raddr = daddr;
+			return -dev->hard_header_len;
+		}
 		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
 		if (mac < 0)
 		{
@@ -163,15 +187,16 @@
  * routing/ARP tables to select a device struct.
  */
 int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
-		struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
+		struct device **dev, int type, struct options *opt,
+		int len, int tos, int ttl, struct rtable ** rp)
 {
 	struct rtable *rt;
 	__u32 raddr;
 	int tmp;
-	__u32 src;
 	struct iphdr *iph;
 	__u32 final_daddr = daddr;
 
+
 	if (opt && opt->srr)
 		daddr = opt->faddr;
 
@@ -183,12 +208,22 @@
 	if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
 		*dev=dev_get(skb->sk->ip_mc_name);
 #endif
+	if (rp)
+	{
+		rt = ip_check_route(rp, daddr, skb->localroute);
+		/*
+		 * If rp != NULL rt_put following below should not
+		 * release route, so that...
+		 */
+		if (rt)
+			ATOMIC_INCR(&rt->rt_refcnt);
+	}
+	else
+		rt = ip_rt_route(daddr, skb->localroute);
+
+
 	if (*dev == NULL)
 	{
-		if(skb->localroute)
-			rt = ip_rt_local(daddr, NULL, &src);
-		else
-			rt = ip_rt_route(daddr, NULL, &src);
 		if (rt == NULL)
 		{
 			ip_statistics.IpOutNoRoutes++;
@@ -196,43 +231,24 @@
 		}
 
 		*dev = rt->rt_dev;
-		/*
-		 *	If the frame is from us and going off machine it MUST MUST MUST
-		 *	have the output device ip address and never the loopback
-		 */
-		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-			saddr = src;/*rt->rt_dev->pa_addr;*/
-		raddr = rt->rt_gateway;
-
 	}
-	else
-	{
-		/*
-		 *	We still need the address of the first hop.
-		 */
-		if(skb->localroute)
-			rt = ip_rt_local(daddr, NULL, &src);
-		else
-			rt = ip_rt_route(daddr, NULL, &src);
-		/*
-		 *	If the frame is from us and going off machine it MUST MUST MUST
-		 *	have the output device ip address and never the loopback
-		 */
-		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-			saddr = src;/*rt->rt_dev->pa_addr;*/
 
-		raddr = (rt == NULL) ? 0 : rt->rt_gateway;
-	}
+	if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr)
+		saddr = rt ? rt->rt_src : (*dev)->pa_addr;
 
-	/*
-	 *	No source addr so make it our addr
-	 */
-	if (saddr == 0)
-		saddr = src;
+	raddr = rt ? rt->rt_gateway : 0;
+
+	if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY))
+	{
+		ip_rt_put(rt);
+		ip_statistics.IpOutNoRoutes++;
+		return -ENETUNREACH;
+	}
 
 	/*
 	 *	No gateway so aim at the real destination
 	 */
+
 	if (raddr == 0)
 		raddr = daddr;
 
@@ -240,10 +256,12 @@
 	 *	Now build the MAC header.
 	 */
 
-	if(type==IPPROTO_TCP)
-		tmp = ip_send_room(skb, raddr, len, *dev, saddr);
+	if (type==IPPROTO_TCP)
+		tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr);
 	else
-		tmp = ip_send(skb, raddr, len, *dev, saddr);
+		tmp = ip_send(rt, skb, raddr, len, *dev, saddr);
+
+	ip_rt_put(rt);
 
 	/*
 	 *	Book keeping
@@ -285,11 +303,6 @@
 
 	if (!opt || !opt->optlen)
 		return sizeof(struct iphdr) + tmp;
-	if (opt->is_strictroute && rt && rt->rt_gateway) 
-	{
-		ip_statistics.IpOutNoRoutes++;
-		return -ENETUNREACH;
-	}
 	iph->ihl += opt->optlen>>2;
 	ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
 	return iph->ihl*4 + tmp;
@@ -563,8 +576,9 @@
 	__u32 saddr;
 	unsigned short id;
 	struct iphdr *iph;
-	int local=0;
-	struct device *dev;
+	__u32 raddr;
+	struct device *dev = NULL;
+	struct hh_cache * hh=NULL;
 	int nfrags=0;
 	__u32 true_daddr = daddr;
 
@@ -588,60 +602,17 @@
 	else
 	{
 #endif	
-		/*
-		 *	Perform the IP routing decisions
-		 */
-	 
-		if(sk->localroute || flags&MSG_DONTROUTE)
-			local=1;
-	
-		rt = sk->ip_route_cache;
-		
-		/*
-		 *	See if the routing cache is outdated. We need to clean this up once we are happy it is reliable
-		 *	by doing the invalidation actively in the route change and header change.
-		 */
-	
-		saddr=sk->ip_route_saddr;	 
-		if(!rt || sk->ip_route_stamp != rt_stamp ||
-		   daddr!=sk->ip_route_daddr || sk->ip_route_local!=local ||
-		   (sk->saddr && sk->saddr != saddr))
-		{
-			if(local)
-				rt = ip_rt_local(daddr, NULL, &saddr);
-			else
-				rt = ip_rt_route(daddr, NULL, &saddr);
-			sk->ip_route_local=local;
-			sk->ip_route_daddr=daddr;
-			sk->ip_route_saddr=saddr;
-			sk->ip_route_stamp=rt_stamp;
-			sk->ip_route_cache=rt;
-			sk->ip_hcache_ver=NULL;
-			sk->ip_hcache_state= 0;
-		}
-		else if(rt)
-		{
-			/*
-			 *	Attempt header caches only if the cached route is being reused. Header cache
-			 *	is not ultra cheap to set up. This means we only set it up on the second packet,
-			 *	so one shot communications are not slowed. We assume (seems reasonable) that 2 is
-			 *	probably going to be a stream of data.
-			 */
-			if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1)
-			{
-				if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver)
-					rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr);
-				else
-					/* Can't cache. Remember this */
-					sk->ip_hcache_state= -1;
-			}
-		}
-		
+		rt = ip_check_route(&sk->ip_route_cache, daddr,
+				    sk->localroute || (flags&MSG_DONTROUTE) ||
+				    (opt && opt->is_strictroute));
 		if (rt == NULL) 
 		{
-	 		ip_statistics.IpOutNoRoutes++;
+			ip_statistics.IpOutNoRoutes++;
 			return(-ENETUNREACH);
 		}
+		saddr = rt->rt_src;
+
+		hh = rt->rt_hh;
 	
 		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
 			saddr = sk->saddr;
@@ -649,10 +620,13 @@
 		dev=rt->rt_dev;
 #ifdef CONFIG_IP_MULTICAST
 	}
+	if (rt && !dev)
+		dev = rt->rt_dev;
 #endif		
 	if (user_saddr)
 		saddr = user_saddr;
 
+	raddr = rt ? rt->rt_gateway : daddr;
 	/*
 	 *	Now compute the buffer space we require
 	 */ 
@@ -662,16 +636,10 @@
 	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
 	 */
 
-	length += 20;
+	length += sizeof(struct iphdr);
 	if (!sk->ip_hdrincl && opt) 
-	{
 		length += opt->optlen;
-		if (opt->is_strictroute && rt && rt->rt_gateway) 
-		{
-			ip_statistics.IpOutNoRoutes++;
-			return -ENETUNREACH;
-		}
-	}
+
 	if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
 	{	
 		int error;
@@ -687,12 +655,19 @@
 		skb->sk=sk;
 		skb->arp=0;
 		skb->saddr=saddr;
-		skb->raddr=(rt&&rt->rt_gateway)?rt->rt_gateway:daddr;
+		skb->raddr = raddr;
 		skb_reserve(skb,(dev->hard_header_len+15)&~15);
-		if(sk->ip_hcache_state>0)
+		if (hh)
 		{
-			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data,dev->hard_header_len);
 			skb->arp=1;
+			memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
+			if (!hh->hh_uptodate)
+			{
+				skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+				printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
+#endif				
+			}
 		}
 		else if(dev->hard_header)
 		{
@@ -747,7 +722,7 @@
 		}
 		return 0;
 	}
-	length-=20;
+	length -= sizeof(struct iphdr);
 	if (sk && !sk->ip_hdrincl && opt) 
 	{
 		length -= opt->optlen;
@@ -847,7 +822,7 @@
 		skb->sk = sk;
 		skb->arp = 0;
 		skb->saddr = saddr;
-		skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr;
+		skb->raddr = raddr;
 		skb_reserve(skb,(dev->hard_header_len+15)&~15);
 		data = skb_put(skb, fraglen-dev->hard_header_len);
 
@@ -858,10 +833,17 @@
 		 *	pointer to speed header cache builds for identical targets.
 		 */
 		 
-		if(sk->ip_hcache_state>0)
+		if (hh)
 		{
-			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data, dev->hard_header_len);
 			skb->arp=1;
+			memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
+			if (!hh->hh_uptodate)
+			{
+				skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+				printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
+#endif				
+			}
 		}
 		else if (dev->hard_header)
 		{
@@ -958,15 +940,15 @@
 			if(sk==NULL || sk->ip_mc_loop)
 			{
 				if(skb->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-					ip_loopback(rt?rt->rt_dev:dev,skb);
+					ip_loopback(dev,skb);
 				else 
 				{
-					struct ip_mc_list *imc=rt?rt->rt_dev->ip_mc_list:dev->ip_mc_list;
+					struct ip_mc_list *imc=dev->ip_mc_list;
 					while(imc!=NULL) 
 					{
 						if(imc->multiaddr==daddr) 
 						{
-							ip_loopback(rt?rt->rt_dev:dev,skb);
+							ip_loopback(dev,skb);
 							break;
 						}
 						imc=imc->next;
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.41/linux/net/ipv4/ip_sockglue.c	Sun Oct 29 11:38:50 1995
+++ linux/net/ipv4/ip_sockglue.c	Tue Nov 14 16:03:58 1995
@@ -283,10 +283,12 @@
 				/*
 				 *	Not set so scan.
 				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
 				{
 					dev=rt->rt_dev;
-					rt->rt_use--;
+					route_src = rt->rt_src;
+					ATOMIC_DECR(&rt->rt_use);
+					ip_rt_put(rt);
 				}
 			}
 			else
@@ -335,10 +337,12 @@
  
 			if(mreq.imr_interface.s_addr==INADDR_ANY) 
 			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
 			        {
 					dev=rt->rt_dev;
-					rt->rt_use--;
+					ATOMIC_DECR(&rt->rt_use);
+					route_src = rt->rt_src;
+					ip_rt_put(rt);
 				}
 			}
 			else 
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v1.3.41/linux/net/ipv4/rarp.c	Thu Nov  9 11:23:56 1995
+++ linux/net/ipv4/rarp.c	Tue Nov 14 16:04:03 1995
@@ -266,7 +266,7 @@
 		sti();
 
 		arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, 
-			dev->dev_addr);
+			dev->dev_addr, sha);
 	}
 	else
 		sti();
@@ -288,6 +288,7 @@
 	int htype, hlen;
 	unsigned long ip;
 	struct rtable *rt;
+	struct device * dev;
   
 	memcpy_fromfs(&r, req, sizeof(r));
   
@@ -326,9 +327,11 @@
  *	Is it reachable directly ?
  */
   
-	rt = ip_rt_route(ip, NULL, NULL);
+	rt = ip_rt_route(ip, 0);
 	if (rt == NULL)
 		return -ENETUNREACH;
+	dev = rt->rt_dev;
+	ip_rt_put(rt);
 
 /*
  *	Is there an existing entry for this address?  Find out...
@@ -366,7 +369,7 @@
 	entry->hlen = hlen;
 	entry->htype = htype;
 	memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
-	entry->dev = rt->rt_dev;
+	entry->dev = dev;
 
 	sti();  
 
@@ -574,5 +577,4 @@
 		rarp_release_entry(rt);
 	}
 }
-
 #endif
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.41/linux/net/ipv4/raw.c	Wed Nov  8 07:11:46 1995
+++ linux/net/ipv4/raw.c	Tue Nov 14 16:04:03 1995
@@ -35,6 +35,8 @@
  *		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 <asm/system.h>
 #include <asm/segment.h>
 #include <linux/types.h>
@@ -57,8 +59,6 @@
 #include <net/udp.h>
 #include <net/checksum.h>
 
-#include <linux/config.h>
-
 #ifdef CONFIG_IP_MROUTE
 struct sock *mroute_socket=NULL;
 #endif
@@ -240,8 +240,8 @@
  *	Temporary
  */
  
-static int raw_sendmsg(struct sock *sk, struct msghdr *msg,
-	int len, int noblock, int flags)
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
+	int flags)
 {
 	if(msg->msg_iovlen==1)
 		return raw_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.41/linux/net/ipv4/route.c	Sun Oct 29 11:38:50 1995
+++ linux/net/ipv4/route.c	Tue Nov 14 16:04:03 1995
@@ -35,6 +35,8 @@
  *		Alan Cox	:	Aligned routing errors more closely with BSD
  *					our system is still very different.
  *		Alan Cox	:	Faster /proc handling
+ *	Alexey Kuznetsov	:	Massive rework to support tree based routing,
+ *					routing caches and better behaviour.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -42,8 +44,10 @@
  *		2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <asm/segment.h>
 #include <asm/system.h>
+#include <asm/bitops.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -65,102 +69,246 @@
 #include <net/netlink.h>
 
 /*
- *	The routing table list
+ * Forwarding Information Base definitions.
  */
 
-static struct rtable *rt_base = NULL;
-unsigned long rt_stamp = 1;		/* Routing table version stamp for caches ( 0 is 'unset' ) */
+struct fib_node
+{
+	struct fib_node		*fib_next;
+	__u32			fib_dst;
+	unsigned long		fib_use;
+	struct fib_info		*fib_info;
+	short			fib_metric;
+	unsigned char		fib_tos;
+};
+
+/*
+ * This structure contains data shared by many of routes.
+ */	
+
+struct fib_info
+{
+	struct fib_info		*fib_next;
+	struct fib_info		*fib_prev;
+	__u32			fib_gateway;
+	struct device		*fib_dev;
+	int			fib_refcnt;
+	unsigned long		fib_window;
+	unsigned short		fib_flags;
+	unsigned short		fib_mtu;
+	unsigned short		fib_irtt;
+};
+
+struct fib_zone
+{
+	struct fib_zone	*fz_next;
+	struct fib_node	**fz_hash_table;
+	struct fib_node	*fz_list;
+	int		fz_nent;
+	int		fz_logmask;
+	__u32		fz_mask;
+};
+
+static struct fib_zone 	*fib_zones[33];
+static struct fib_zone 	*fib_zone_list;
+static struct fib_node 	*fib_loopback = NULL;
+static struct fib_info 	*fib_info_list;
 
 /*
- *	Pointer to the loopback route
+ * Backlogging.
  */
- 
-static struct rtable *rt_loopback = NULL;
+
+#define RT_BH_REDIRECT		0
+#define RT_BH_GARBAGE_COLLECT 	1
+#define RT_BH_FREE	 	2
+
+struct rt_req
+{
+	struct rt_req * rtr_next;
+	struct device *dev;
+	__u32 dst;
+	__u32 gw;
+	unsigned char tos;
+};
+
+int		    	ip_rt_lock;
+unsigned		ip_rt_bh_mask;
+static struct rt_req 	*rt_backlog;
 
 /*
- *	Remove a routing table entry.
+ * Route cache.
  */
 
-static int rt_del(__u32 dst, __u32 mask,
-		char *devname, __u32 gtw, short rt_flags, short metric)
+struct rtable 		*ip_rt_hash_table[RT_HASH_DIVISOR];
+static int		rt_cache_size;
+static struct rtable 	*rt_free_queue;
+struct wait_queue	*rt_wait;
+
+static void rt_kick_backlog(void);
+static void rt_cache_add(unsigned hash, struct rtable * rth);
+static void rt_cache_flush(void);
+static void rt_garbage_collect_1(void);
+
+/* 
+ * Evaluate mask length.
+ */
+
+static __inline__ int rt_logmask(__u32 mask)
 {
-	struct rtable *r, **rp;
-	unsigned long flags;
-	int found=0;
+	if (!(mask = ntohl(mask)))
+		return 32;
+	return ffz(~mask);
+}
 
-	rp = &rt_base;
-	
-	/*
-	 *	This must be done with interrupts off because we could take
-	 *	an ICMP_REDIRECT.
-	 */
-	 
-	save_flags(flags);
-	cli();
-	while((r = *rp) != NULL) 
+/* 
+ * Create mask from length.
+ */
+
+static __inline__ __u32 rt_mask(int logmask)
+{
+	if (logmask >= 32)
+		return 0;
+	return htonl(~((1<<logmask)-1));
+}
+
+static __inline__ unsigned fz_hash_code(__u32 dst, int logmask)
+{
+	return ip_rt_hash_code(ntohl(dst)>>logmask);
+}
+
+/*
+ * Free FIB node.
+ */
+
+static void fib_free_node(struct fib_node * f)
+{
+	struct fib_info * fi = f->fib_info;
+	if (!--fi->fib_refcnt)
 	{
-		/*
-		 *	Make sure the destination and netmask match.
-		 *	metric, gateway and device are also checked
-		 *	if they were specified.
-		 */
-		if (r->rt_dst != dst ||
-		    (mask && r->rt_mask != mask) ||
-		    (gtw && r->rt_gateway != gtw) ||
-		    (metric >= 0 && r->rt_metric != metric) ||
-		    (devname && strcmp((r->rt_dev)->name,devname) != 0) )
+#if RT_CACHE_DEBUG >= 2
+		printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+		if (fi->fib_next)
+			fi->fib_next->fib_prev = fi->fib_prev;
+		if (fi->fib_prev)
+			fi->fib_prev->fib_next = fi->fib_next;
+		if (fi == fib_info_list)
+			fib_info_list = fi->fib_next;
+	}
+	kfree_s(f, sizeof(struct fib_node));
+}
+
+/*
+ * Find gateway route by address.
+ */
+
+static struct fib_node * fib_lookup_gateway(__u32 dst)
+{
+	struct fib_zone * fz;
+	struct fib_node * f;
+
+	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+	{
+		if (fz->fz_hash_table)
+			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+		else
+			f = fz->fz_list;
+		
+		for ( ; f; f = f->fib_next)
 		{
-			rp = &r->rt_next;
-			continue;
+			if ((dst ^ f->fib_dst) & fz->fz_mask)
+				continue;
+			if (f->fib_info->fib_flags & RTF_GATEWAY)
+				return NULL;
+			return f;
 		}
-		*rp = r->rt_next;
-		
-		/*
-		 *	If we delete the loopback route update its pointer.
-		 */
-		 
-		if (rt_loopback == r)
-			rt_loopback = NULL;
-		ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, rt_flags, metric, r->rt_dev->name);
-		kfree_s(r, sizeof(struct rtable));
-		found=1;
-	} 
-	rt_stamp++;		/* New table revision */
-	
-	restore_flags(flags);
-	
-	if(found)
-		return 0;
-	return -ESRCH;
+	}
+	return NULL;
 }
 
+/*
+ * Find local route by address.
+ * FIXME: I use "longest match" principle. If destination
+ *	  has some non-local route, I'll not search shorter matches.
+ *	  It's possible, I'm wrong, but I wanted to prevent following
+ *	  situation:
+ *	route add 193.233.7.128 netmask 255.255.255.192 gw xxxxxx
+ *	route add 193.233.7.0	netmask 255.255.255.0 eth1
+ *	  (Two ethernets connected by serial line, one is small and other is large)
+ *	  Host 193.233.7.129 is locally unreachable,
+ *	  but old (<=1.3.37) code will send packets destined for it to eth1.
+ *
+ */
+
+static struct fib_node * fib_lookup_local(__u32 dst)
+{
+	struct fib_zone * fz;
+	struct fib_node * f;
+
+	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+	{
+		int longest_match_found = 0;
+
+		if (fz->fz_hash_table)
+			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+		else
+			f = fz->fz_list;
+		
+		for ( ; f; f = f->fib_next)
+		{
+			if ((dst ^ f->fib_dst) & fz->fz_mask)
+				continue;
+			if (!(f->fib_info->fib_flags & RTF_GATEWAY))
+				return f;
+			longest_match_found = 1;
+		}
+		if (longest_match_found)
+			return NULL;
+	}
+	return NULL;
+}
 
 /*
- *	Remove all routing table entries for a device. This is called when
- *	a device is downed.
+ * Main lookup routine.
+ *	IMPORTANT NOTE: this algorithm has small difference from <=1.3.37 visible
+ *	by user. It doesn't route non-CIDR broadcasts by default.
+ *
+ *	F.e.
+ *		ifconfig eth0 193.233.7.65 netmask 255.255.255.192 broadcast 193.233.7.255
+ *	is valid, but if you really are not able (not allowed, do not want) to
+ *	use CIDR compliant broadcast 193.233.7.127, you should add host route:
+ *		route add -host 193.233.7.255 eth0
  */
- 
-void ip_rt_flush(struct device *dev)
+
+static struct fib_node * fib_lookup(__u32 dst)
 {
-	struct rtable *r;
-	struct rtable **rp;
-	unsigned long flags;
+	struct fib_zone * fz;
+	struct fib_node * f;
 
-	rp = &rt_base;
-	save_flags(flags);
-	cli();
-	while ((r = *rp) != NULL) {
-		if (r->rt_dev != dev) {
-			rp = &r->rt_next;
-			continue;
+	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+	{
+		if (fz->fz_hash_table)
+			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+		else
+			f = fz->fz_list;
+		
+		for ( ; f; f = f->fib_next)
+		{
+			if ((dst ^ f->fib_dst) & fz->fz_mask)
+				continue;
+			return f;
 		}
-		*rp = r->rt_next;
-		if (rt_loopback == r)
-			rt_loopback = NULL;
-		kfree_s(r, sizeof(struct rtable));
-	} 
-	rt_stamp++;		/* New table revision */
-	restore_flags(flags);
+	}
+	return NULL;
+}
+
+static __inline__ struct device * get_gw_dev(__u32 gw)
+{
+	struct fib_node * f;
+	f = fib_lookup_gateway(gw);
+	if (f)
+		return f->fib_info->fib_dev;
+	return NULL;
 }
 
 /*
@@ -199,228 +347,1244 @@
 }
 
 
-/*
- *	Find the route entry through which our gateway will be reached
- */
- 
-static inline struct device * get_gw_dev(__u32 gw)
+/*
+ *	Check if a mask is acceptable.
+ */
+ 
+static inline int bad_mask(__u32 mask, __u32 addr)
+{
+	if (addr & (mask = ~mask))
+		return 1;
+	mask = ntohl(mask);
+	if (mask & (mask+1))
+		return 1;
+	return 0;
+}
+
+
+static int fib_del_list(struct fib_node **fp, __u32 dst,
+		struct device * dev, __u32 gtw, short flags, short metric, __u32 mask)
+{
+	struct fib_node *f;
+	int found=0;
+
+	while((f = *fp) != NULL) 
+	{
+		struct fib_info * fi = f->fib_info;
+
+		/*
+		 *	Make sure the destination and netmask match.
+		 *	metric, gateway and device are also checked
+		 *	if they were specified.
+		 */
+		if (f->fib_dst != dst ||
+		    (gtw && fi->fib_gateway != gtw) ||
+		    (metric >= 0 && f->fib_metric != metric) ||
+		    (dev && fi->fib_dev != dev) )
+		{
+			fp = &f->fib_next;
+			continue;
+		}
+		cli();
+		*fp = f->fib_next;
+		if (fib_loopback == f)
+			fib_loopback = NULL;
+		sti();
+		ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, flags, metric, fi->fib_dev->name);
+		fib_free_node(f);
+		found++;
+	}
+	return found;
+}
+
+static __inline__ int fib_del_1(__u32 dst, __u32 mask,
+		struct device * dev, __u32 gtw, short flags, short metric)
+{
+	struct fib_node **fp;
+	struct fib_zone *fz;
+	int found=0;
+
+	if (!mask)
+	{
+		for (fz=fib_zone_list; fz; fz = fz->fz_next)
+		{
+			int tmp;
+			if (fz->fz_hash_table)
+				fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+			else
+				fp = &fz->fz_list;
+
+			tmp = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
+			fz->fz_nent -= tmp;
+			found += tmp;
+		}
+	} 
+	else
+	{
+		if ((fz = fib_zones[rt_logmask(mask)]) != NULL)
+		{
+			if (fz->fz_hash_table)
+				fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+			else
+				fp = &fz->fz_list;
+	
+			found = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
+			fz->fz_nent -= found;
+		}
+	}
+
+	if (found)
+	{
+		rt_cache_flush();
+		return 0;
+	}
+	return -ESRCH;
+}
+
+
+static struct fib_info * fib_create_info(__u32 gw, struct device * dev,
+					 unsigned short flags, unsigned short mss,
+					 unsigned long window, unsigned short irtt)
+{
+	struct fib_info * fi;
+
+	if (!(flags & RTF_MSS))
+	{
+		mss = dev->mtu;
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
+		/*
+		 *	If MTU was not specified, use default.
+		 *	If you want to increase MTU for some net (local subnet)
+		 *	use "route add .... mss xxx".
+		 *
+		 * 	The MTU isnt currently always used and computed as it
+		 *	should be as far as I can tell. [Still verifying this is right]
+		 */
+		if ((flags & RTF_GATEWAY) && mss > 576)
+			mss = 576;
+#endif
+	}
+	if (!(flags & RTF_WINDOW))
+		window = 0;
+	if (!(flags & RTF_IRTT))
+		irtt = 0;
+
+	for (fi=fib_info_list; fi; fi = fi->fib_next)
+	{
+		if (fi->fib_gateway != gw ||
+		    fi->fib_dev != dev  ||
+		    fi->fib_flags != flags ||
+		    fi->fib_mtu != mss ||
+		    fi->fib_window != window ||
+		    fi->fib_irtt != irtt)
+			continue;
+		fi->fib_refcnt++;
+#if RT_CACHE_DEBUG >= 2
+		printk("fib_create_info: fi %08x/%s is duplicate\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+		return fi;
+	}
+	fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
+	if (!fi)
+		return NULL;
+	memset(fi, 0, sizeof(struct fib_info));
+	fi->fib_flags = flags;
+	fi->fib_dev = dev;
+	fi->fib_gateway = gw;
+	fi->fib_mtu = mss;
+	fi->fib_window = window;
+	fi->fib_refcnt++;
+	fi->fib_next = fib_info_list;
+	fi->fib_prev = NULL;
+	if (fib_info_list)
+		fib_info_list->fib_prev = fi;
+	fib_info_list = fi;
+#if RT_CACHE_DEBUG >= 2
+	printk("fib_create_info: fi %08x/%s is created\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+	return fi;
+}
+
+
+static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask,
+	__u32 gw, struct device *dev, unsigned short mss,
+	unsigned long window, unsigned short irtt, short metric)
+{
+	struct fib_node *f, *f1;
+	struct fib_node **fp;
+	struct fib_node **dup_fp = NULL;
+	struct fib_zone * fz;
+	struct fib_info * fi;
+	int logmask;
+
+	if (flags & RTF_HOST) 
+		mask = 0xffffffff;
+	/*
+	 * If mask is not specified, try to guess it.
+	 */
+	else if (!mask)
+	{
+		if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
+		{
+			mask = dev->pa_mask;
+			flags &= ~RTF_GATEWAY;
+			if (flags & RTF_DYNAMIC) 
+			{
+				printk("Dynamic route to my own net rejected\n");
+				return;
+			}
+		} 
+		else
+			mask = guess_mask(dst, dev);
+		dst &= mask;
+	}
+	
+	/*
+	 *	A gateway must be reachable and not a local address
+	 */
+	 
+	if (gw == dev->pa_addr)
+		flags &= ~RTF_GATEWAY;
+		
+	if (flags & RTF_GATEWAY) 
+	{
+		/*
+		 *	Don't try to add a gateway we can't reach.. 
+		 */
+		 
+		if (dev != get_gw_dev(gw))
+			return;
+			
+		flags |= RTF_GATEWAY;
+	} 
+	else
+		gw = 0;
+		
+	/*
+	 *	Allocate an entry and fill it in.
+	 */
+	 
+	f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
+	if (f == NULL)
+		return;
+
+	memset(f, 0, sizeof(struct fib_node));
+	f->fib_dst = dst;
+	f->fib_metric = metric;
+	f->fib_tos    = 0;
+
+	if  ((fi = fib_create_info(gw, dev, flags, mss, window, irtt)) == NULL)
+	{
+		kfree_s(f, sizeof(struct fib_node));
+		return;
+	}
+	f->fib_info = fi;
+
+	logmask = rt_logmask(mask);
+	fz = fib_zones[logmask];
+
+
+	if (!fz)
+	{
+		int i;
+		fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
+		if (!fz)
+		{
+			fib_free_node(f);
+			return;
+		}
+		memset(fz, 0, sizeof(struct fib_zone));
+		fz->fz_logmask = logmask;
+		fz->fz_mask = mask;
+		for (i=logmask-1; i>=0; i--)
+			if (fib_zones[i])
+				break;
+		cli();
+		if (i<0)
+		{
+			fz->fz_next = fib_zone_list;
+			fib_zone_list = fz;
+		}
+		else
+		{
+			fz->fz_next = fib_zones[i]->fz_next;
+			fib_zones[i]->fz_next = fz;
+		}
+		fib_zones[logmask] = fz;
+		sti();
+	}
+
+	/*
+	 * If zone overgrows RTZ_HASHING_LIMIT, create hash table.
+	 */
+
+	if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32)
+	{
+		struct fib_node ** ht;
+#if RT_CACHE_DEBUG
+		printk("fib_add_1: hashing for zone %d started\n", logmask);
+#endif
+		ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL);
+
+		if (ht)
+		{
+			memset(ht, 0, RTZ_HASH_DIVISOR*sizeof(struct fib_node*));
+			cli();
+			f1 = fz->fz_list;
+			while (f1)
+			{
+				struct fib_node * next;
+				unsigned hash = fz_hash_code(f1->fib_dst, logmask);
+				next = f1->fib_next;
+				f1->fib_next = ht[hash];
+				ht[hash] = f1;
+				f1 = next;
+			}
+			fz->fz_list = NULL;
+			fz->fz_hash_table = ht; 
+			sti();
+		}
+	}
+
+	if (fz->fz_hash_table)
+		fp = &fz->fz_hash_table[fz_hash_code(dst, logmask)];
+	else
+		fp = &fz->fz_list;
+
+	/*
+	 * Scan list to find the first route with the same destination
+	 */
+	while ((f1 = *fp) != NULL)
+	{
+		if (f1->fib_dst == dst)
+			break;
+		fp = &f1->fib_next;
+	}
+
+	/*
+	 * Find route with the same destination and less (or equal) metric.
+	 */
+	while ((f1 = *fp) != NULL && f1->fib_dst == dst)
+	{
+		if (f1->fib_metric >= metric)
+			break;
+		/*
+		 *	Record route with the same destination and gateway,
+		 *	but less metric. We'll delete it 
+		 *	after instantiation of new route.
+		 */
+		if (f1->fib_info->fib_gateway == gw)
+			dup_fp = fp;
+		fp = &f1->fib_next;
+	}
+
+	/*
+	 * Is it already present?
+	 */
+
+	if (f1 && f1->fib_metric == metric && f1->fib_info == fi)
+	{
+		fib_free_node(f);
+		return;
+	}
+	
+	/*
+	 * Insert new entry to the list.
+	 */
+
+	cli();
+	f->fib_next = f1;
+	*fp = f;
+	if (!fib_loopback && (fi->fib_dev->flags & IFF_LOOPBACK))
+		fib_loopback = f;
+	sti();
+	fz->fz_nent++;
+	ip_netlink_msg(RTMSG_NEWROUTE, dst, gw, mask, flags, metric, fi->fib_dev->name);
+
+	/*
+	 *	Delete route with the same destination and gateway.
+	 *	Note that we should have at most one such route.
+	 */
+	if (dup_fp)
+		fp = dup_fp;
+	else
+		fp = &f->fib_next;
+
+	while ((f1 = *fp) != NULL && f1->fib_dst == dst)
+	{
+		if (f1->fib_info->fib_gateway == gw)
+		{
+			cli();
+			*fp = f1->fib_next;
+			if (fib_loopback == f1)
+				fib_loopback = NULL;
+			sti();
+			ip_netlink_msg(RTMSG_DELROUTE, dst, gw, mask, flags, metric, f1->fib_info->fib_dev->name);
+			fib_free_node(f1);
+			fz->fz_nent--;
+			break;
+		}
+		fp = &f1->fib_next;
+	}
+	rt_cache_flush();
+	return;
+}
+
+static int rt_flush_list(struct fib_node ** fp, struct device *dev)
+{
+	int found = 0;
+	struct fib_node *f;
+
+	while ((f = *fp) != NULL) {
+		if (f->fib_info->fib_dev != dev) {
+			fp = &f->fib_next;
+			continue;
+		}
+		cli();
+		*fp = f->fib_next;
+		if (fib_loopback == f)
+			fib_loopback = NULL;
+		sti();
+		fib_free_node(f);
+		found++;
+	}
+	return found;
+}
+
+static __inline__ void fib_flush_1(struct device *dev)
+{
+	struct fib_zone *fz;
+	int found = 0;
+
+	for (fz = fib_zone_list; fz; fz = fz->fz_next)
+	{
+		if (fz->fz_hash_table)
+		{
+			int i;
+			int tmp = 0;
+			for (i=0; i<RTZ_HASH_DIVISOR; i++)
+				tmp += rt_flush_list(&fz->fz_hash_table[i], dev);
+			fz->fz_nent -= tmp;
+			found += tmp;
+		}
+		else
+		{
+			int tmp;
+			tmp = rt_flush_list(&fz->fz_list, dev);
+			fz->fz_nent -= tmp;
+			found += tmp;
+		}
+	}
+		
+	if (found)
+		rt_cache_flush();
+}
+
+
+/* 
+ *	Called from the PROCfs module. This outputs /proc/net/route.
+ *
+ *	We preserve the old format but pad the buffers out. This means that
+ *	we can spin over the other entries as we read them. Remember the
+ *	gated BGP4 code could need to read 60,000+ routes on occasion (thats
+ *	about 7Mb of data). To do that ok we will need to also cache the
+ *	last route we got to (reads will generally be following on from
+ *	one another without gaps).
+ */
+ 
+int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct fib_zone *fz;
+	struct fib_node *f;
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	int i;
+	
+	pos = 128;
+
+	if (offset<128)
+	{
+		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
+		len = 128;
+  	}
+  	
+	while  (ip_rt_lock)
+		sleep_on(&rt_wait);
+	ip_rt_fast_lock();
+
+	for (fz=fib_zone_list; fz; fz = fz->fz_next)
+	{
+		int maxslot;
+		struct fib_node ** fp;
+
+		if (fz->fz_nent == 0)
+			continue;
+
+		if (pos + 128*fz->fz_nent <= offset)
+		{
+			pos += 128*fz->fz_nent;
+			len = 0;
+			continue;
+		}
+
+		if (fz->fz_hash_table)
+		{
+			maxslot = RTZ_HASH_DIVISOR;
+			fp	= fz->fz_hash_table;
+		}
+		else
+		{
+			maxslot	= 1;
+			fp	= &fz->fz_list;
+		}
+			
+		for (i=0; i < maxslot; i++, fp++)
+		{
+			
+			for (f = *fp; f; f = f->fib_next) 
+			{
+				struct fib_info * fi;
+				/*
+				 *	Spin through entries until we are ready
+				 */
+				pos += 128;
+
+				if (pos <= offset)
+				{
+					len=0;
+					continue;
+				}
+					
+				fi = f->fib_info;
+				sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
+					fi->fib_dev->name, (unsigned long)f->fib_dst, (unsigned long)fi->fib_gateway,
+					fi->fib_flags, 0, f->fib_use, f->fib_metric,
+					(unsigned long)fz->fz_mask, (int)fi->fib_mtu, fi->fib_window, (int)fi->fib_irtt);
+				sprintf(buffer+len,"%-127s\n",temp);
+
+				len += 128;
+				if (pos >= offset+length)
+					goto done;
+			}
+		}
+        }
+
+done:
+	ip_rt_unlock();
+	wake_up(&rt_wait);
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos - offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	struct rtable *r;
+	int i;
+
+	pos = 128;
+
+	if (offset<128)
+	{
+		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tHH\tARP\n");
+		len = 128;
+  	}
+	
+  	
+	while  (ip_rt_lock)
+		sleep_on(&rt_wait);
+	ip_rt_fast_lock();
+
+	for (i = 0; i<RT_HASH_DIVISOR; i++)
+	{
+		for (r = ip_rt_hash_table[i]; r; r = r->rt_next) 
+		{
+			/*
+			 *	Spin through entries until we are ready
+			 */
+			pos += 128;
+
+			if (pos <= offset)
+			{
+				len = 0;
+				continue;
+			}
+					
+			sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%ld\t%lu\t%d\t%08lX\t%d\t%lu\t%u\t%ld\t%1d",
+				r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
+				r->rt_flags, r->rt_refcnt, r->rt_use, 0,
+				(unsigned long)r->rt_src, (int)r->rt_mtu, r->rt_window, (int)r->rt_irtt, r->rt_hh ? r->rt_hh->hh_refcnt : -1, r->rt_hh ? r->rt_hh->hh_uptodate : 0);
+			sprintf(buffer+len,"%-127s\n",temp);
+			len += 128;
+			if (pos >= offset+length)
+				goto done;
+		}
+        }
+
+done:
+	ip_rt_unlock();
+	wake_up(&rt_wait);
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos-offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+
+static void rt_free(struct rtable * rt)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (!rt->rt_refcnt)
+	{
+		struct hh_cache * hh = rt->rt_hh;
+		rt->rt_hh = NULL;
+		if (hh && !--hh->hh_refcnt)
+		{
+			restore_flags(flags);
+			kfree_s(hh, sizeof(struct hh_cache));
+		}
+		restore_flags(flags);
+		kfree_s(rt, sizeof(struct rt_table));
+		return;
+	}
+	rt->rt_next = rt_free_queue;
+	rt->rt_flags &= ~RTF_UP;
+	rt_free_queue = rt;
+	ip_rt_bh_mask |= RT_BH_FREE;
+#if RT_CACHE_DEBUG >= 2
+	printk("rt_free: %08x\n", rt->rt_dst);
+#endif
+	restore_flags(flags);
+}
+
+/*
+ * RT "bottom half" handlers. Called with masked inetrrupts.
+ */
+
+static __inline__ void rt_kick_free_queue(void)
+{
+	struct rtable *rt, **rtp;
+
+	rtp = &rt_free_queue;
+
+	while ((rt = *rtp) != NULL)
+	{
+		if  (!rt->rt_refcnt)
+		{
+			struct hh_cache * hh = rt->rt_hh;
+#if RT_CACHE_DEBUG >= 2
+			__u32 daddr = rt->rt_dst;
+#endif
+			*rtp = rt->rt_next;
+			rt->rt_hh = NULL;
+			if (hh && !--hh->hh_refcnt)
+			{
+				sti();
+				kfree_s(hh, sizeof(struct hh_cache));
+			}
+			sti();
+			kfree_s(rt, sizeof(struct rt_table));
+#if RT_CACHE_DEBUG >= 2
+			printk("rt_kick_free_queue: %08x is free\n", daddr);
+#endif
+			cli();
+			continue;
+		}
+		rtp = &rt->rt_next;
+	}
+}
+
+void ip_rt_run_bh() {
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if (ip_rt_bh_mask && !ip_rt_lock)
+	{
+		if (ip_rt_bh_mask & RT_BH_REDIRECT)
+			rt_kick_backlog();
+
+		if (ip_rt_bh_mask & RT_BH_GARBAGE_COLLECT)
+		{
+			ip_rt_fast_lock();
+			ip_rt_bh_mask &= ~RT_BH_GARBAGE_COLLECT;
+			sti();
+			rt_garbage_collect_1();
+			cli();
+			ip_rt_fast_unlock();
+		}
+
+		if (ip_rt_bh_mask & RT_BH_FREE)
+			rt_kick_free_queue();
+	}
+	restore_flags(flags);
+}
+
+
+void ip_rt_check_expire()
+{
+	ip_rt_fast_lock();
+	if (ip_rt_lock == 1)
+	{
+		int i;
+		struct rtable *rth, **rthp;
+		unsigned long flags;
+		unsigned long now = jiffies;
+
+		save_flags(flags);
+		for (i=0; i<RT_HASH_DIVISOR; i++)
+		{
+			rthp = &ip_rt_hash_table[i];
+
+			while ((rth = *rthp) != NULL)
+			{
+				struct rtable * rth_next = rth->rt_next;
+
+				/*
+				 * Cleanup aged off entries.
+				 */
+
+				cli();
+				if (!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
+				{
+					*rthp = rth_next;
+					sti();
+					rt_cache_size--;
+#if RT_CACHE_DEBUG >= 2
+					printk("rt_check_expire clean %02x@%08x\n", i, rth->rt_dst);
+#endif
+					rt_free(rth);
+					continue;
+				}
+				sti();
+
+				if (!rth_next)
+					break;
+
+				/*
+				 * LRU ordering.
+				 */
+
+				if (rth->rt_lastuse + RT_CACHE_BUBBLE_THRESHOULD < rth_next->rt_lastuse ||
+				    (rth->rt_lastuse < rth_next->rt_lastuse &&
+				     rth->rt_use < rth_next->rt_use))
+				{
+#if RT_CACHE_DEBUG >= 2
+					printk("rt_check_expire bubbled %02x@%08x<->%08x\n", i, rth->rt_dst, rth_next->rt_dst);
+#endif
+					cli();
+					*rthp = rth_next;
+					rth->rt_next = rth_next->rt_next;
+					rth_next->rt_next = rth;
+					sti();
+					rthp = &rth_next->rt_next;
+					continue;
+				}
+				rthp = &rth->rt_next;
+			}
+		}
+		restore_flags(flags);
+		rt_kick_free_queue();
+	}
+	ip_rt_unlock();
+}
+
+static void rt_redirect_1(__u32 dst, __u32 gw, struct device *dev)
+{
+	struct rtable *rt;
+	unsigned long hash = ip_rt_hash_code(dst);
+
+	if (gw == dev->pa_addr)
+		return;
+	if (dev != get_gw_dev(gw))
+		return;
+	rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+	if (rt == NULL) 
+		return;
+	memset(rt, 0, sizeof(struct rtable));
+	rt->rt_flags = RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY | RTF_UP;
+	rt->rt_dst = dst;
+	rt->rt_dev = dev;
+	rt->rt_gateway = gw;
+	rt->rt_src = dev->pa_addr;
+	rt->rt_mtu = dev->mtu;
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
+	if (dev->mtu > 576)
+		rt->rt_mtu = 576;
+#endif
+	rt->rt_lastuse  = jiffies;
+	rt->rt_refcnt  = 1;
+	rt_cache_add(hash, rt);
+	ip_rt_put(rt);
+	return;
+}
+
+static void rt_cache_flush(void)
+{
+	int i;
+	struct rtable * rth, * next;
+
+	for (i=0; i<RT_HASH_DIVISOR; i++)
+	{
+		int nr=0;
+
+		cli();
+		if (!(rth = ip_rt_hash_table[i]))
+		{
+			sti();
+			continue;
+		}
+
+		ip_rt_hash_table[i] = NULL;
+		sti();
+
+		for (; rth; rth=next)
+		{
+			next = rth->rt_next;
+			rt_cache_size--;
+			nr++;
+			rth->rt_next = NULL;
+			rt_free(rth);
+		}
+#if RT_CACHE_DEBUG >= 2
+		if (nr > 0)
+			printk("rt_cache_flush: %d@%02x\n", nr, i);
+#endif
+	}
+#if RT_CACHE_DEBUG >= 1
+	if (rt_cache_size)
+	{
+		printk("rt_cache_flush: bug rt_cache_size=%d\n", rt_cache_size);
+		rt_cache_size = 0;
+	}
+#endif
+}
+
+static void rt_garbage_collect_1(void)
+{
+	int i;
+	unsigned expire = RT_CACHE_TIMEOUT>>1;
+	struct rtable * rth, **rthp;
+	unsigned long now = jiffies;
+
+	for (;;)
+	{
+		for (i=0; i<RT_HASH_DIVISOR; i++)
+		{
+			if (!ip_rt_hash_table[i])
+				continue;
+			for (rthp=&ip_rt_hash_table[i]; (rth=*rthp); rthp=&rth->rt_next)
+			{
+				if (rth->rt_lastuse + expire*(rth->rt_refcnt+1) > now)
+					continue;
+				rt_cache_size--;
+				cli();
+				*rthp=rth->rt_next;
+				rth->rt_next = NULL;
+				sti();
+				rt_free(rth);
+				break;
+			}
+		}
+		if (rt_cache_size < RT_CACHE_SIZE_MAX)
+			return;
+		expire >>= 1;
+	}
+}
+
+static __inline__ void rt_req_enqueue(struct rt_req **q, struct rt_req *rtr)
+{
+	unsigned long flags;
+	struct rt_req * tail;
+
+	save_flags(flags);
+	cli();
+	tail = *q;
+	if (!tail)
+		rtr->rtr_next = rtr;
+	else
+	{
+		rtr->rtr_next = tail->rtr_next;
+		tail->rtr_next = rtr;
+	}
+	*q = rtr;
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * Caller should mask interrupts.
+ */
+
+static __inline__ struct rt_req * rt_req_dequeue(struct rt_req **q)
+{
+	struct rt_req * rtr;
+
+	if (*q)
+	{
+		rtr = (*q)->rtr_next;
+		(*q)->rtr_next = rtr->rtr_next;
+		if (rtr->rtr_next == rtr)
+			*q = NULL;
+		rtr->rtr_next = NULL;
+		return rtr;
+	}
+	return NULL;
+}
+
+/*
+   Called with masked interrupts
+ */
+
+static void rt_kick_backlog()
+{
+	if (!ip_rt_lock)
+	{
+		struct rt_req * rtr;
+
+		ip_rt_fast_lock();
+
+		while ((rtr = rt_req_dequeue(&rt_backlog)) != NULL)
+		{
+			sti();
+			rt_redirect_1(rtr->dst, rtr->gw, rtr->dev);
+			kfree_s(rtr, sizeof(struct rt_req));
+			cli();
+		}
+
+		ip_rt_bh_mask &= ~RT_BH_REDIRECT;
+
+		ip_rt_fast_unlock();
+	}
+}
+
+/*
+ * rt_{del|add|flush} called only from USER process. Waiting is OK.
+ */
+
+static int rt_del(__u32 dst, __u32 mask,
+		struct device * dev, __u32 gtw, short rt_flags, short metric)
+{
+	int retval;
+
+	while (ip_rt_lock)
+		sleep_on(&rt_wait);
+	ip_rt_fast_lock();
+	retval = fib_del_1(dst, mask, dev, gtw, rt_flags, metric);
+	ip_rt_unlock();
+	wake_up(&rt_wait);
+	return retval;
+}
+
+static void rt_add(short flags, __u32 dst, __u32 mask,
+	__u32 gw, struct device *dev, unsigned short mss,
+	unsigned long window, unsigned short irtt, short metric)
+{
+	while (ip_rt_lock)
+		sleep_on(&rt_wait);
+	ip_rt_fast_lock();
+	fib_add_1(flags, dst, mask, gw, dev, mss, window, irtt, metric);
+	ip_rt_unlock();
+	wake_up(&rt_wait);
+}
+
+void ip_rt_flush(struct device *dev)
+{
+	while (ip_rt_lock)
+		sleep_on(&rt_wait);
+	ip_rt_fast_lock();
+	fib_flush_1(dev);
+	ip_rt_unlock();
+	wake_up(&rt_wait);
+}
+
+/*
+   Called by ICMP module.
+ */
+
+void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev)
+{
+	struct rt_req * rtr;
+	struct rtable * rt;
+
+	rt = ip_rt_route(dst, 0);
+	if (!rt)
+		return;
+
+	if (rt->rt_gateway != src ||
+	    rt->rt_dev != dev ||
+	    ((gw^dev->pa_addr)&dev->pa_mask) ||
+	    ip_chk_addr(gw))
+	{
+		ip_rt_put(rt);
+		return;
+	}
+	ip_rt_put(rt);
+
+	ip_rt_fast_lock();
+	if (ip_rt_lock == 1)
+	{
+		rt_redirect_1(dst, gw, dev);
+		ip_rt_unlock();
+		return;
+	}
+
+	rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC);
+	if (rtr)
+	{
+		rtr->dst = dst;
+		rtr->gw = gw;
+		rtr->dev = dev;
+		rt_req_enqueue(&rt_backlog, rtr);
+		ip_rt_bh_mask |= RT_BH_REDIRECT;
+	}
+	ip_rt_unlock();
+}
+
+
+static __inline__ void rt_garbage_collect(void)
 {
-	struct rtable * rt;
-
-	for (rt = rt_base ; ; rt = rt->rt_next) 
+	if (ip_rt_lock == 1)
 	{
-		if (!rt)
-			return NULL;
-		if ((gw ^ rt->rt_dst) & rt->rt_mask)
-			continue;
-		/* 
-		 *	Gateways behind gateways are a no-no 
-		 */
-		 
-		if (rt->rt_flags & RTF_GATEWAY)
-			return NULL;
-		return rt->rt_dev;
+		rt_garbage_collect_1();
+		return;
 	}
+	ip_rt_bh_mask |= RT_BH_GARBAGE_COLLECT;
 }
 
-/*
- *	Rewrote rt_add(), as the old one was weird - Linus
- *
- *	This routine is used to update the IP routing table, either
- *	from the kernel (ICMP_REDIRECT) or via an ioctl call issued
- *	by the superuser.
- */
- 
-void ip_rt_add(short flags, __u32 dst, __u32 mask,
-	__u32 gw, struct device *dev, unsigned short mtu,
-	unsigned long window, unsigned short irtt, short metric)
+static void rt_cache_add(unsigned hash, struct rtable * rth)
 {
-	struct rtable *r, *rt;
-	struct rtable **rp;
-	unsigned long cpuflags;
-	int duplicate = 0;
+	unsigned long	flags;
+	struct rtable	**rthp;
+	__u32		daddr = rth->rt_dst;
+	unsigned long	now = jiffies;
 
-	/*
-	 *	A host is a unique machine and has no network bits.
-	 */
-	 
-	if (flags & RTF_HOST) 
+#if RT_CACHE_DEBUG >= 2
+	if (ip_rt_lock != 1)
 	{
-		mask = 0xffffffff;
-	} 
-	
-	/*
-	 *	Calculate the network mask
-	 */
-	 
-	else if (!mask) 
+		printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock);
+		return;
+	}
+#endif
+
+	save_flags(flags);
+
+	if (rth->rt_dev->header_cache_bind)
 	{
-		if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
+		struct rtable * rtg = rth;
+
+		if (rth->rt_gateway != daddr)
 		{
-			mask = dev->pa_mask;
-			flags &= ~RTF_GATEWAY;
-			if (flags & RTF_DYNAMIC) 
+			ip_rt_fast_unlock();
+			rtg = ip_rt_route(rth->rt_gateway, 0);
+			ip_rt_fast_lock();
+		}
+
+		if (rtg)
+		{
+			if (rtg == rth)
+				rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst);
+			else
 			{
-				/*printk("Dynamic route to my own net rejected\n");*/
-				return;
+				if (rtg->rt_hh)
+					ATOMIC_INCR(&rtg->rt_hh->hh_refcnt);
+				rth->rt_hh = rtg->rt_hh;
+				ip_rt_put(rtg);
 			}
-		} 
-		else
-			mask = guess_mask(dst, dev);
-		dst &= mask;
+		}
 	}
-	
-	/*
-	 *	A gateway must be reachable and not a local address
-	 */
-	 
-	if (gw == dev->pa_addr)
-		flags &= ~RTF_GATEWAY;
-		
-	if (flags & RTF_GATEWAY) 
+
+	if (rt_cache_size >= RT_CACHE_SIZE_MAX)
+		rt_garbage_collect();
+
+	cli();
+	rth->rt_next = ip_rt_hash_table[hash];
+#if RT_CACHE_DEBUG >= 2
+	if (rth->rt_next)
 	{
-		/*
-		 *	Don't try to add a gateway we can't reach.. 
-		 */
-		 
-		if (dev != get_gw_dev(gw))
-			return;
-			
-		flags |= RTF_GATEWAY;
-	} 
-	else
-		gw = 0;
-		
+		struct rtable * trth;
+		printk("rt_cache @%02x: %08x", hash, daddr);
+		for (trth=rth->rt_next; trth; trth=trth->rt_next)
+			printk(" . %08x", trth->rt_dst);
+		printk("\n");
+	}
+#endif
+	ip_rt_hash_table[hash] = rth;
+	rthp = &rth->rt_next;
+	sti();
+	rt_cache_size++;
+
 	/*
-	 *	Allocate an entry and fill it in.
+	 * Cleanup duplicate (and aged off) entries.
 	 */
-	 
-	rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
-	if (rt == NULL) 
+
+	while ((rth = *rthp) != NULL)
 	{
-		return;
+
+		cli();
+		if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
+		    || rth->rt_dst == daddr)
+		{
+			*rthp = rth->rt_next;
+			rt_cache_size--;
+			sti();
+#if RT_CACHE_DEBUG >= 2
+			printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst);
+#endif
+			rt_free(rth);
+			continue;
+		}
+		sti();
+		rthp = &rth->rt_next;
 	}
-	memset(rt, 0, sizeof(struct rtable));
-	rt->rt_flags = flags | RTF_UP;
-	rt->rt_dst = dst;
-	rt->rt_dev = dev;
-	rt->rt_gateway = gw;
-	rt->rt_mask = mask;
-	rt->rt_mss = dev->mtu - HEADER_SIZE;
-	rt->rt_metric = metric;
-	rt->rt_window = 0;	/* Default is no clamping */
+	restore_flags(flags);
+}
 
-	/* Are the MSS/Window valid ? */
+/*
+   RT should be already locked.
+   
+   We could improve this by keeping a chain of say 32 struct rtable's
+   last freed for fast recycling.
+   
+ */
 
-	if(rt->rt_flags & RTF_MSS)
-		rt->rt_mss = mtu;
-		
-	if(rt->rt_flags & RTF_WINDOW)
-		rt->rt_window = window;
-	if(rt->rt_flags & RTF_IRTT)
-		rt->rt_irtt = irtt;
+struct rtable * ip_rt_slow_route (__u32 daddr, int local)
+{
+	unsigned hash = ip_rt_hash_code(daddr)^local;
+	struct rtable * rth;
+	struct fib_node * f;
+	struct fib_info * fi;
+	__u32 saddr;
+
+#if RT_CACHE_DEBUG >= 2
+	printk("rt_cache miss @%08x\n", daddr);
+#endif
 
-	/*
-	 *	What we have to do is loop though this until we have
-	 *	found the first address which has a higher generality than
-	 *	the one in rt.  Then we can put rt in right before it.
-	 *	The interrupts must be off for this process.
-	 */
+	rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+	if (!rth)
+	{
+		ip_rt_unlock();
+		return NULL;
+	}
 
-	save_flags(cpuflags);
-	cli();
+	if (local)
+		f = fib_lookup_local(daddr);
+	else
+		f = fib_lookup (daddr);
 
-	/*
-	 *	Remove old route if we are getting a duplicate. 
-	 */
-	 
-	rp = &rt_base;
-	while ((r = *rp) != NULL) 
+	if (f)
 	{
-		if (r->rt_dst != dst || 
-		    r->rt_mask != mask)
-		{
-			rp = &r->rt_next;
-			continue;
-		}
-		if (r->rt_metric != metric && r->rt_gateway != gw)
+		fi = f->fib_info;
+		f->fib_use++;
+	}
+
+	if (!f || (fi->fib_flags & RTF_REJECT))
+	{
+#if RT_CACHE_DEBUG >= 2
+		printk("rt_route failed @%08x\n", daddr);
+#endif
+		ip_rt_unlock();
+		kfree_s(rth, sizeof(struct rtable));
+		return NULL;
+	}
+
+	saddr = fi->fib_dev->pa_addr;
+
+	if (daddr == fi->fib_dev->pa_addr)
+	{
+		f->fib_use--;
+		if ((f = fib_loopback) != NULL)
 		{
-			duplicate = 1;
-			rp = &r->rt_next;
-			continue;
+			f->fib_use++;
+			fi = f->fib_info;
 		}
-		*rp = r->rt_next;
-		if (rt_loopback == r)
-			rt_loopback = NULL;
-		ip_netlink_msg(RTMSG_DELROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name);
-		kfree_s(r, sizeof(struct rtable));
 	}
-	
-	/*
-	 *	Add the new route 
-	 */
-	 
-	rp = &rt_base;
-	while ((r = *rp) != NULL) {
-		/*
-		 * When adding a duplicate route, add it before
-		 * the route with a higher metric.
-		 */
-		if (duplicate &&
-		    r->rt_dst == dst &&
-		    r->rt_mask == mask &&
-		    r->rt_metric > metric)
-			break;
-		else
-		/*
-		 * Otherwise, just add it before the
-		 * route with a higher generality.
-		 */
-			if ((r->rt_mask & mask) != mask)
-				break;
-		rp = &r->rt_next;
+
+	if (!f)
+	{
+		ip_rt_unlock();
+		kfree_s(rth, sizeof(struct rtable));
+		return NULL;
 	}
-	rt->rt_next = r;
-	*rp = rt;
-	
-	/*
-	 *	Update the loopback route
-	 */
-	 
-	if ((rt->rt_dev->flags & IFF_LOOPBACK) && !rt_loopback)
-		rt_loopback = rt;
 
-	rt_stamp++;		/* New table revision */
-		
-	/*
-	 *	Restore the interrupts and return
-	 */
+	rth->rt_dst	= daddr;
+	rth->rt_src	= saddr;
+	rth->rt_lastuse	= jiffies;
+	rth->rt_refcnt	= 1;
+	rth->rt_use	= 1;
+	rth->rt_next	= NULL;
+	rth->rt_hh	= NULL;
+	rth->rt_gateway	= fi->fib_gateway;
+	rth->rt_dev	= fi->fib_dev;
+	rth->rt_mtu	= fi->fib_mtu;
+	rth->rt_window	= fi->fib_window;
+	rth->rt_irtt	= fi->fib_irtt;
+	rth->rt_tos	= f->fib_tos;
+	rth->rt_flags   = fi->fib_flags | RTF_HOST;
+	if (local)
+		rth->rt_flags   |= RTF_LOCAL;
 
-	restore_flags(cpuflags);
-	ip_netlink_msg(RTMSG_NEWROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name);
-	return;
+	if (!(rth->rt_flags & RTF_GATEWAY))
+		rth->rt_gateway = rth->rt_dst;
+
+	if (ip_rt_lock == 1)
+		rt_cache_add(hash, rth);
+	else
+	{
+		rt_free(rth);
+#if RT_CACHE_DEBUG >= 1
+		printk("rt_cache: route to %08x was born dead\n", daddr);
+#endif
+	}
+
+	ip_rt_unlock();
+	return rth;
 }
 
+void ip_rt_put(struct rtable * rt)
+{
+	if (rt)
+		ATOMIC_DECR(&rt->rt_refcnt);
+}
 
-/*
- *	Check if a mask is acceptable.
- */
- 
-static inline int bad_mask(__u32 mask, __u32 addr)
+struct rtable * ip_rt_route(__u32 daddr, int local)
 {
-	if (addr & (mask = ~mask))
-		return 1;
-	mask = ntohl(mask);
-	if (mask & (mask+1))
-		return 1;
-	return 0;
+	struct rtable * rth;
+
+	ip_rt_fast_lock();
+
+	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
+	{
+		if (rth->rt_dst == daddr)
+		{
+			rth->rt_lastuse = jiffies;
+			ATOMIC_INCR(&rth->rt_use);
+			ATOMIC_INCR(&rth->rt_refcnt);
+			ip_rt_unlock();
+			return rth;
+		}
+	}
+	return ip_rt_slow_route (daddr, local);
 }
 
+
 /*
- *	Process a route add request from the user
+ *	Process a route add request from the user, or from a kernel
+ *	task.
  */
  
-static int rt_new(struct rtentry *r)
+int ip_rt_new(struct rtentry *r)
 {
 	int err;
 	char * devname;
@@ -465,7 +1629,7 @@
 	/*
 	 *	BSD emulation: Permits route add someroute gw one-of-my-addresses
 	 *	to indicate which iface. Not as clean as the nice Linux dev technique
-	 *	but people keep using it... 
+	 *	but people keep using it...  (and gated likes it ;))
 	 */
 	 
 	if (!dev && (flags & RTF_GATEWAY)) 
@@ -522,8 +1686,8 @@
 	/*
 	 *	Add the route
 	 */
-	 
-	ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
+
+	rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
 	return 0;
 }
 
@@ -539,6 +1703,7 @@
 	struct sockaddr_in *gtw;
 	char *devname;
 	int err;
+	struct device * dev = NULL;
 
 	trg = (struct sockaddr_in *) &r->rt_dst;
 	msk = (struct sockaddr_in *) &r->rt_genmask;
@@ -548,159 +1713,20 @@
 		err = getname(devname, &devname);
 		if (err)
 			return err;
+		dev = dev_get(devname);
+		putname(devname);
+		if (!dev)
+			return -ENODEV;
 	}
 	/*
 	 * metric can become negative here if it wasn't filled in
 	 * but that's a fortunate accident; we really use that in rt_del.
 	 */
-	err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, devname,
+	err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, dev,
 		(__u32)gtw->sin_addr.s_addr, r->rt_flags, r->rt_metric - 1);
-	if ( devname != NULL )
-		putname(devname);
 	return err;
 }
 
-
-/* 
- *	Called from the PROCfs module. This outputs /proc/net/route.
- *
- *	We preserve the old format but pad the buffers out. This means that
- *	we can spin over the other entries as we read them. Remember the
- *	gated BGP4 code could need to read 60,000+ routes on occasion (thats
- *	about 7Mb of data). To do that ok we will need to also cache the
- *	last route we got to (reads will generally be following on from
- *	one another without gaps).
- */
- 
-int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct rtable *r;
-	int len=128;
-	off_t pos=0;
-	off_t begin=0;
-	char temp[129];
-
-	if(offset<128)
-		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
-	pos=128;
-  	
-	for (r = rt_base; r != NULL; r = r->rt_next) 
-	{
-		/*
-		 *	Spin through entries until we are ready
-		 */
-		if(pos+128<offset)
-		{
-			pos+=128;
-			continue;
-		}
-					
-        	sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
-			r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
-			r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
-			(unsigned long)r->rt_mask, (int)r->rt_mss, r->rt_window, (int)r->rt_irtt);
-		sprintf(buffer+len,"%-127s\n",temp);
-		len+=128;
-		pos+=128;
-		if(pos<offset)
-		{
-			len=0;
-			begin=pos;
-		}
-		if(pos>offset+length)
-			break;
-  	}
-  	
-  	*start=buffer+(offset-begin);
-  	len-=(offset-begin);
-  	if(len>length)
-  		len=length;
-  	return len;
-}
-
-/*
- *	This is hackish, but results in better code. Use "-S" to see why.
- */
- 
-#define early_out ({ goto no_route; 1; })
-
-/*
- *	Route a packet. This needs to be fairly quick. Florian & Co. 
- *	suggested a unified ARP and IP routing cache. Done right its
- *	probably a brilliant idea. I'd actually suggest a unified
- *	ARP/IP routing/Socket pointer cache. Volunteers welcome
- */
- 
-struct rtable * ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr)
-{
-	struct rtable *rt;
-
-	for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) 
-	{
-		if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
-			break;
-		/*
-		 *	broadcast addresses can be special cases.. 
-		 */
-		if (rt->rt_flags & RTF_GATEWAY)
-			continue;		 
-		if ((rt->rt_dev->flags & IFF_BROADCAST) &&
-		    (rt->rt_dev->pa_brdaddr == daddr))
-			break;
-	}
-	
-	if(rt->rt_flags&RTF_REJECT)
-		return NULL;
-	
-	if(src_addr!=NULL)
-		*src_addr= rt->rt_dev->pa_addr;
-		
-	if (daddr == rt->rt_dev->pa_addr) {
-		if ((rt = rt_loopback) == NULL)
-			goto no_route;
-	}
-	rt->rt_use++;
-	return rt;
-no_route:
-	return NULL;
-}
-
-struct rtable * ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr)
-{
-	struct rtable *rt;
-
-	for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) 
-	{
-		/*
-		 *	No routed addressing.
-		 */
-		if (rt->rt_flags&RTF_GATEWAY)
-			continue;
-			
-		if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
-			break;
-		/*
-		 *	broadcast addresses can be special cases.. 
-		 */
-		 
-		if ((rt->rt_dev->flags & IFF_BROADCAST) &&
-		     rt->rt_dev->pa_brdaddr == daddr)
-			break;
-	}
-	
-	if(src_addr!=NULL)
-		*src_addr= rt->rt_dev->pa_addr;
-		
-	if (daddr == rt->rt_dev->pa_addr) {
-		if ((rt = rt_loopback) == NULL)
-			goto no_route;
-	}
-	rt->rt_use++;
-	return rt;
-no_route:
-	return NULL;
-}
-
 /*
  *	Handle IP routing ioctl calls. These are used to manipulate the routing tables
  */
@@ -720,8 +1746,15 @@
 			if (err)
 				return err;
 			memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
-			return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
+			return (cmd == SIOCDELRT) ? rt_kill(&rt) : ip_rt_new(&rt);
 	}
 
 	return -EINVAL;
 }
+
+void ip_rt_advice(struct rtable **rp, int advice)
+{
+	/* Thanks! */
+	return;
+}
+
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.41/linux/net/ipv4/tcp.c	Sat Nov 11 17:41:35 1995
+++ linux/net/ipv4/tcp.c	Tue Nov 14 16:09:50 1995
@@ -176,7 +176,9 @@
  *		Marc Tamsky	:	Closing in closing fixes.
  *		Mike Shaver	:	RFC1122 verifications.
  *		Alan Cox	:	rcv_saddr errors.
- *		Alan Cox	:	Block double connect()
+ *		Alan Cox	:	Block double connect().
+ *		Alan Cox	:	Small hooks for enSKIP.
+ *		Alexey Kuznetsov:	Path MTU discovery.
  *
  *
  * To Fix:
@@ -184,8 +186,6 @@
  *		so it doesn't iterate over the queue, also spot packets with no funny
  *		options arriving in order and process directly.
  *
- *		Implement RFC 1191 [Path MTU discovery]
- *		Look at the effect of implementing RFC 1337 suggestions and their impact.
  *		Rewrite output state machine to use a single queue and do low window
  *		situations as per the spec (RFC 1122)
  *		Speed up input assembly algorithm.
@@ -674,19 +674,25 @@
 		 *	currently this is done (less efficiently) elsewhere.
 		 */
 
-		iph->id = htons(ip_id_count++);
-		ip_send_check(iph);
-		
 		/*
 		 *	Put a MAC header back on (may cause ARPing)
 		 */
 		 
-		if(skb->localroute)
-			rt=ip_rt_local(iph->daddr,NULL,NULL);
-		else
-			rt=ip_rt_route(iph->daddr,NULL,NULL);
+	        {
+			/* ANK: UGLY, but the bug, that was here, should be fixed.
+			 */
+			struct options *  opt = (struct options*)skb->proto_priv;
+			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute);
+	        }
+
+		iph->id = htons(ip_id_count++);
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+		if (rt && ntohs(iph->tot_len) > rt->rt_mtu)
+			iph->frag_off &= ~htons(IP_DF);
+#endif
+		ip_send_check(iph);
 			
-		if(rt==NULL)	/* Deep poo */
+		if (rt==NULL)	/* Deep poo */
 		{
 			if(skb->sk)
 			{
@@ -698,11 +704,20 @@
 		{
 			dev=rt->rt_dev;
 			skb->raddr=rt->rt_gateway;
-			if(skb->raddr==0)
-				skb->raddr=iph->daddr;
 			skb->dev=dev;
 			skb->arp=1;
-			if(dev->hard_header)
+			if (rt->rt_hh)
+			{
+				memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+				if (!rt->rt_hh->hh_uptodate)
+				{
+					skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+					printk("tcp_do_retransmit: hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway);
+#endif
+				}
+			}
+			else if (dev->hard_header)
 			{
 				if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, skb->len)<0)
 					skb->arp=0;
@@ -869,8 +884,7 @@
 		 *	Attempt to recover if arp has changed (unlikely!) or
 		 *	a route has shifted (not supported prior to 1.3).
 		 */
-		arp_destroy (sk->daddr, 0);
-		/*ip_route_check (sk->daddr);*/
+		ip_rt_advice(&sk->ip_route_cache, 0);
 	}
 	
 	/*
@@ -1040,13 +1054,15 @@
 void tcp_err(int type, int code, unsigned char *header, __u32 daddr,
 	__u32 saddr, struct inet_protocol *protocol)
 {
-	struct tcphdr *th;
+	struct tcphdr *th = (struct tcphdr *)header;
 	struct sock *sk;
-	struct iphdr *iph=(struct iphdr *)header;
-  
-	header+=4*iph->ihl;
-   
-
+	
+	/*
+	 *	This one is _WRONG_. FIXME urgently.
+	 */
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY	 
+	struct iphdr *iph=(struct iphdr *)(header-sizeof(struct iphdr));
+#endif  
 	th =(struct tcphdr *)header;
 	sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
 
@@ -1071,6 +1087,27 @@
 		sk->error_report(sk);
 	}
 
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+	{
+		struct rtable * rt;
+		/*
+		 * Ugly trick to pass MTU to protocol layer.
+		 * Really we should add argument "info" to error handler.
+		 */
+		unsigned short new_mtu = ntohs(iph->id);
+
+		if ((rt = sk->ip_route_cache) != NULL)
+			if (rt->rt_mtu > new_mtu)
+				rt->rt_mtu = new_mtu;
+
+		if (sk->mtu > new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr))
+			sk->mtu = new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+
+		return;
+	}
+#endif
+
 	/*
 	 * If we've already connected we will keep trying
 	 * until we time out, or the user gives up.
@@ -1553,7 +1590,7 @@
 	 */
 	 
 	tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
-				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	if (tmp < 0) 
 	{
   		buff->free = 1;
@@ -1787,6 +1824,24 @@
 		/* 
 		 *	Now we need to check if we have a half built packet. 
 		 */
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+		/*
+		 *	FIXME:  I'm almost sure that this fragment is BUG,
+		 *		but it works... I do not know why 8) --ANK
+		 *
+		 *	Really, we should rebuild all the queues...
+		 *	It's difficult. Temprorary hack is to send all
+		 *	queued segments with allowed fragmentation.
+		 */
+		{
+			int new_mss = min(sk->mtu, sk->max_window);
+			if (new_mss < sk->mss)
+			{
+				tcp_send_partial(sk);
+				sk->mss = new_mss;
+			}
+		}
+#endif
 	
 			if ((skb = tcp_dequeue_partial(sk)) != NULL) 
 			{
@@ -1800,11 +1855,10 @@
 				if (!(flags & MSG_OOB)) 
 				{
 					copy = min(sk->mss - (skb->len - hdrlen), seglen);
-					/* FIXME: this is really a bug. */
 					if (copy <= 0) 
 					{
-						printk("TCP: **bug**: \"copy\" <= 0: %d - (%ld - %d) <= %d\n", sk->mss, skb->len, hdrlen, seglen);
-				  		copy = 0;
+						printk("TCP: **bug**: \"copy\" <= 0\n");
+				  		return -EFAULT;
 					}		  
 					memcpy_fromfs(skb_put(skb,copy), from, copy);
 					from += copy;
@@ -1922,7 +1976,7 @@
 			 */
 		
 			tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
-				 IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl);
+				 IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 			if (tmp < 0 ) 
 			{
 				sock_wfree(sk, skb);
@@ -1931,6 +1985,9 @@
 					return(copied);
 				return(tmp);
 			}
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+			skb->ip_hdr->frag_off |= htons(IP_DF);
+#endif
 			skb->dev = dev;
 			skb->h.th =(struct tcphdr *)skb_put(skb,sizeof(struct tcphdr));
 			tmp = tcp_build_header(skb->h.th, sk, seglen-copy);
@@ -2038,7 +2095,7 @@
 	 */
 
 	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-			       IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+			       IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	if (tmp < 0) 
 	{
   		buff->free = 1;
@@ -2560,7 +2617,7 @@
 
 	tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
 			   IPPROTO_TCP, sk->opt,
-			   sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
+			   sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	if (tmp < 0) 
 	{
 		int t;
@@ -2715,7 +2772,7 @@
 	 */
 
 	tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
-			   sizeof(struct tcphdr),tos,ttl);
+			   sizeof(struct tcphdr),tos,ttl,NULL);
 	if (tmp < 0) 
 	{
   		buff->free = 1;
@@ -2919,6 +2976,7 @@
 
 	memcpy(newsk, sk, sizeof(*newsk));
 	newsk->opt = NULL;
+	newsk->ip_route_cache  = NULL;
 	if (opt && opt->optlen) {
 	  sk->opt = (struct options*)kmalloc(sizeof(struct options)+opt->optlen, GFP_ATOMIC);
 	  if (!sk->opt) {
@@ -3022,7 +3080,8 @@
 	 * 	Note use of sk->user_mss, since user has no direct access to newsk 
 	 */
 
-	rt=ip_rt_route(saddr, NULL,NULL);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	newsk->ip_route_cache = rt;
 	
 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
 		newsk->window_clamp = rt->rt_window;
@@ -3031,19 +3090,10 @@
 		
 	if (sk->user_mss)
 		newsk->mtu = sk->user_mss;
-	else if(rt!=NULL && (rt->rt_flags&RTF_MSS))
-		newsk->mtu = rt->rt_mss - sizeof(struct iphdr) - sizeof(struct tcphdr);
+	else if (rt)
+		newsk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
 	else 
-	{
-#ifdef CONFIG_INET_SNARL	/* Sub Nets Are Local */
-		if ((saddr ^ daddr) & default_mask(saddr))
-#else
-		if ((saddr ^ daddr) & dev->pa_mask)
-#endif
-			newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
-		else
-			newsk->mtu = MAX_WINDOW;
-	}
+		newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
 
 	/*
 	 *	But not bigger than device MTU 
@@ -3051,6 +3101,20 @@
 
 	newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 
+#ifdef CONFIG_SKIP
+	
+	/*
+	 *	SKIP devices set their MTU to 65535. This is so they can take packets
+	 *	unfragmented to security process then fragment. They could lie to the
+	 *	TCP layer about a suitable MTU, but its easier to let skip sort it out
+	 *	simply because the final package we want unfragmented is going to be
+	 *
+	 *	[IPHDR][IPSP][Security data][Modified TCP data][Security data]
+	 */
+	 
+	if(skip_pick_mtu!=NULL)		/* If SKIP is loaded.. */
+		sk->mtu=skip_pick_mtu(sk->mtu,dev);
+#endif
 	/*
 	 *	This will min with what arrived in the packet 
 	 */
@@ -3080,7 +3144,7 @@
 	 */
 
 	tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev,
-			       IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
+			       IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&newsk->ip_route_cache);
 
 	/*
 	 *	Something went wrong. 
@@ -3281,6 +3345,13 @@
 			iph = skb->ip_hdr;
 			th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
 			size = skb->len - (((unsigned char *) th) - skb->data);
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+			if (size > sk->mtu - sizeof(struct iphdr))
+			{
+				iph->frag_off &= ~htons(IP_DF);
+				ip_send_check(iph);
+			}
+#endif
 			
 			th->ack_seq = ntohl(sk->acked_seq);
 			th->window = ntohs(tcp_select_window(sk));
@@ -4522,29 +4593,17 @@
 	 *	Put in the IP header and routing stuff.
 	 */
 	 
-	if (sk->localroute)
-		rt=ip_rt_local(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr);
-	else
-		rt=ip_rt_route(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr);
-
-	/*
-	 *	When we connect we enforce receive requirements too.
-	 */
-	 
-	sk->rcv_saddr=sk->saddr;
-	
-	/*
-	 *	We need to build the routing stuff from the things saved in skb. 
-	 */
-
 	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-					IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
+		IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	if (tmp < 0) 
 	{
 		sock_wfree(sk, buff);
 		release_sock(sk);
 		return(-ENETUNREACH);
 	}
+	if ((rt = sk->ip_route_cache) != NULL && !sk->saddr)
+		sk->saddr = rt->rt_src;
+	sk->rcv_saddr = sk->saddr;
 
 	t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
 
@@ -4571,19 +4630,11 @@
 
 	if (sk->user_mss)
 		sk->mtu = sk->user_mss;
-	else if(rt!=NULL && (rt->rt_flags&RTF_MSS))
-		sk->mtu = rt->rt_mss;
+	else if (rt)
+		sk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
 	else 
-	{
-#ifdef CONFIG_INET_SNARL
-		if ((sk->saddr ^ sk->daddr) & default_mask(sk->saddr))
-#else
-		if ((sk->saddr ^ sk->daddr) & dev->pa_mask)
-#endif
-			sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
-		else
-			sk->mtu = MAX_WINDOW;
-	}
+		sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
+
 	/*
 	 *	but not bigger than device MTU 
 	 */
@@ -4592,6 +4643,21 @@
 		sk->mtu = 32;	/* Sanity limit */
 		
 	sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
+
+#ifdef CONFIG_SKIP
+	
+	/*
+	 *	SKIP devices set their MTU to 65535. This is so they can take packets
+	 *	unfragmented to security process then fragment. They could lie to the
+	 *	TCP layer about a suitable MTU, but its easier to let skip sort it out
+	 *	simply because the final package we want unfragmented is going to be
+	 *
+	 *	[IPHDR][IPSP][Security data][Modified TCP data][Security data]
+	 */
+	 
+	if(skip_pick_mtu!=NULL)		/* If SKIP is loaded.. */
+		sk->mtu=skip_pick_mtu(sk->mtu,dev);
+#endif
 	
 	/*
 	 *	Put in the TCP options to say MTU. 
@@ -5222,7 +5288,7 @@
 
 	    	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
 					 IPPROTO_TCP, sk->opt, buff->truesize,
-					 sk->ip_tos,sk->ip_ttl);
+					 sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	    	if (tmp < 0) 
 	    	{
 			sock_wfree(sk, buff);
@@ -5321,7 +5387,7 @@
 		 */
 		 
 		tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 		if (tmp < 0) 
 		{
 			sock_wfree(sk, buff);
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v1.3.41/linux/net/ipv4/timer.c	Mon Jul 31 15:59:04 1995
+++ linux/net/ipv4/timer.c	Tue Nov 14 16:04:05 1995
@@ -146,8 +146,6 @@
 			/* We've waited long enough, close the socket. */
 			sk->state = TCP_CLOSE;
 			delete_timer (sk);
-			/* Kill the ARP entry in case the hardware has changed. */
-			arp_destroy (sk->daddr, 0);
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->shutdown = SHUTDOWN_MASK;
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.41/linux/net/ipv4/udp.c	Wed Nov  8 07:11:46 1995
+++ linux/net/ipv4/udp.c	Tue Nov 14 16:04:05 1995
@@ -400,8 +400,8 @@
  *	Temporary
  */
  
-static int udp_sendmsg(struct sock *sk, struct msghdr *msg,
-	int len, int noblock, int flags)
+static int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
+	int flags)
 {
 	if(msg->msg_iovlen==1)
 		return udp_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
@@ -541,7 +541,6 @@
 int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
 {
 	struct rtable *rt;
-	__u32 sa;
 	if (addr_len < sizeof(*usin)) 
 	  	return(-EINVAL);
 
@@ -553,19 +552,18 @@
 	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
 		return -EACCES;			/* Must turn broadcast on first */
   	
-  	rt=(sk->localroute?ip_rt_local:ip_rt_route)((__u32)usin->sin_addr.s_addr, NULL, &sa);
-  	if(rt==NULL)
+  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
+  	if (rt==NULL)
   		return -ENETUNREACH;
   	if(!sk->saddr)
-	  	sk->saddr = sa;		/* Update source address */
+	  	sk->saddr = rt->rt_src;		/* Update source address */
 	if(!sk->rcv_saddr)
-		sk->rcv_saddr = sa;
+		sk->rcv_saddr = rt->rt_src;
 	sk->daddr = usin->sin_addr.s_addr;
 	sk->dummy_th.dest = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
 	udp_cache_zap();
 	sk->ip_route_cache = rt;
-	sk->ip_route_stamp = rt_stamp;
 	return(0);
 }
 
diff -u --recursive --new-file v1.3.41/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.41/linux/net/ipx/af_ipx.c	Wed Nov  8 07:11:46 1995
+++ linux/net/ipx/af_ipx.c	Tue Nov 14 16:04:05 1995
@@ -304,7 +304,7 @@
 }
 
 static int 
-ipxitf_device_event(unsigned long event, void *ptr)
+ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
 {
 	struct device *dev = ptr;
 	ipx_interface *i, *tmp;
diff -u --recursive --new-file v1.3.41/linux/net/netlink.c linux/net/netlink.c
--- v1.3.41/linux/net/netlink.c	Thu Nov  9 11:23:56 1995
+++ linux/net/netlink.c	Tue Nov 14 16:04:05 1995
@@ -216,8 +216,8 @@
 {
 	int ct;
 
-	if(register_chrdev(NET_MAJOR,"netlink", &netlink_fops)) {
-		printk("netlink: unable to get major %d\n", NET_MAJOR);
+	if(register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {
+		printk("netlink: unable to get major %d\n", NETLINK_MAJOR);
 		return -EIO;
 	}
 	for(ct=0;ct<MAX_LINKS;ct++)
diff -u --recursive --new-file v1.3.41/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v1.3.41/linux/net/netrom/nr_dev.c	Wed Sep 27 16:00:02 1995
+++ linux/net/netrom/nr_dev.c	Tue Nov 14 16:04:05 1995
@@ -112,7 +112,7 @@
 
 	skb_device_unlock(skb);
 
-	if (!arp_query(bp + 7, raddr, ARPHRD_NETROM)) {
+	if (!arp_query(bp + 7, raddr, dev)) {
 		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 		return 1;
diff -u --recursive --new-file v1.3.41/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v1.3.41/linux/net/netrom/nr_route.c	Mon Oct 23 18:02:24 1995
+++ linux/net/netrom/nr_route.c	Tue Nov 14 16:04:05 1995
@@ -524,7 +524,7 @@
 		return dev;
 
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-		if (arp_query((unsigned char *)&callsign, dev->pa_addr, ARPHRD_AX25))
+		if (arp_query((unsigned char *)&callsign, dev->pa_addr, dev))
 			return dev;
 	
 	return NULL;
diff -u --recursive --new-file v1.3.41/linux/net/socket.c linux/net/socket.c
--- v1.3.41/linux/net/socket.c	Wed Nov  8 07:11:46 1995
+++ linux/net/socket.c	Thu Nov 16 09:11:38 1995
@@ -878,12 +878,12 @@
 	if(err)
 		return err;
 		
+	iov.iov_base=buff;
+	iov.iov_len=len;
 	msg.msg_name=NULL;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_accrights=NULL;
-	iov.iov_base=buff;
-	iov.iov_len=1;
 	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
 }
 
