diff -u --recursive --new-file v1.1.85/linux/CREDITS linux/CREDITS
--- v1.1.85/linux/CREDITS	Thu Jan 26 07:49:02 1995
+++ linux/CREDITS	Fri Jan 27 12:35:01 1995
@@ -508,6 +508,13 @@
 E: Kai.Makisara@vtt.fi
 D: SCSI Tape Driver
 
+N: Hamish Macdonald
+E: hamish@border.ocunix.on.ca
+D: Linux/68k port
+S: 102-B Valleystream Drive
+S: Nepean, Ontario
+S: Canada K2H-9E1
+
 N: Peter MacDonald
 D: SLS distribution
 D: Initial implementation of VC's, pty's and select()
@@ -914,6 +921,7 @@
 D: Linux News (electronic magazine)
 D: Meta-FAQ, originator
 D: INFO-SHEET, former maintainer
+D: Author of the longest-living linux bug
 S: Ohratie 16 C 198
 S: sf-01370 Vantaa
 S: Finland
diff -u --recursive --new-file v1.1.85/linux/Makefile linux/Makefile
--- v1.1.85/linux/Makefile	Thu Jan 26 07:49:02 1995
+++ linux/Makefile	Fri Jan 27 11:10:45 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 85
+SUBLEVEL = 86
 
 ARCH = i386
 
@@ -228,10 +228,6 @@
 	rm -f vmlinux System.map
 	rm -f .tmp* drivers/sound/configure
 	rm -fr modules/*
-ifdef CONFIG_MODVERSIONS
-	rm -f $(TOPDIR)/include/linux/modversions.h
-	rm -f $(TOPDIR)/include/linux/modules/*
-endif
 
 mrproper: clean
 	rm -f include/linux/autoconf.h include/linux/version.h
@@ -239,6 +235,10 @@
 	rm -f .version .config* config.in config.old
 	rm -f include/asm
 	rm -f .depend `find . -name .depend -print`
+ifdef CONFIG_MODVERSIONS
+	rm -f $(TOPDIR)/include/linux/modversions.h
+	rm -f $(TOPDIR)/include/linux/modules/*
+endif
 
 distclean: mrproper
 
diff -u --recursive --new-file v1.1.85/linux/README linux/README
--- v1.1.85/linux/README	Wed Jan 11 21:14:24 1995
+++ linux/README	Fri Jan 27 10:51:52 1995
@@ -111,11 +111,12 @@
  - keep a backup kernel handy in case something goes wrong. 
 
  - In order to boot your new kernel, you'll need to copy the kernel
-   image (found in /usr/src/linux/zImage after compilation) to the place
-   where your regular bootable kernel is found. 
+   image (found in /usr/src/linux/arch/i386/boot/zImage after compilation)
+   to the place where your regular bootable kernel is found. 
 
    For some, this is on a floppy disk, in which case you can "cp
-   /usr/src/linux/zImage /dev/fd0" to make a bootable floppy. 
+   /usr/src/linux/arch/i386/boot/zImage /dev/fd0" to make a bootable
+   floppy. 
 
    If you boot Linux from the hard drive, chances are you use LILO which
    uses the kernel image as specified in the file /etc/lilo/config.  The
diff -u --recursive --new-file v1.1.85/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.1.85/linux/arch/i386/config.in	Thu Jan 26 07:49:02 1995
+++ linux/arch/i386/config.in	Thu Jan 26 17:34:47 1995
@@ -9,7 +9,7 @@
 bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
 bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y
 if [ "$CONFIG_ST506" = "y" ]; then
-  comment 'Please see block/drivers/README.ide for help/info on IDE drives'
+  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
   bool '   Use old (reliable) disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y
   if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
     bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
@@ -53,6 +53,7 @@
 bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
 fi
 bool 'The IPX protocol' CONFIG_IPX n
+#bool 'Appletalk DDP' CONFIG_ATALK n
 #bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
 fi
 
@@ -109,7 +110,7 @@
 comment 'Skipping network driver configuration options...'
 
 else
-bool 'Dummy net driver support' CONFIG_DUMMY n
+bool 'Dummy net driver support' CONFIG_DUMMY y
 bool 'SLIP (serial line) support' CONFIG_SLIP n
 if [ "$CONFIG_SLIP" = "y" ]; then
   bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
@@ -160,6 +161,7 @@
 		bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
 	fi
 	bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
+	bool 'DE425, DE434, DE435 support' CONFIG_DE4x5 n
 #	bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
 #	bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
 #	bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
diff -u --recursive --new-file v1.1.85/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v1.1.85/linux/arch/i386/kernel/setup.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/i386/kernel/setup.c	Fri Jan 27 11:18:18 1995
@@ -54,7 +54,7 @@
 unsigned char aux_device_present;
 extern int ramdisk_size;
 extern int root_mountflags;
-extern int end;
+extern int etext, edata, end;
 
 extern char empty_zero_page[PAGE_SIZE];
 
@@ -95,6 +95,10 @@
 	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;
+	init_task.mm->end_data = TASK_SIZE + (unsigned long) &edata;
+	init_task.mm->brk = TASK_SIZE + (unsigned long) &end;
 
 	for (;;) {
 		if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") {
diff -u --recursive --new-file v1.1.85/linux/arch/mips/Makefile linux/arch/mips/Makefile
--- v1.1.85/linux/arch/mips/Makefile	Mon Jan 16 14:18:13 1995
+++ linux/arch/mips/Makefile	Wed Jan 25 08:54:22 1995
@@ -1,5 +1,5 @@
 #
-# mips/Makefile
+# arch/mips/Makefile
 #
 # This file is included by the global makefile so that you can add your own
 # architecture-specific flags and dependencies. Remember to do have actions
@@ -20,18 +20,23 @@
 LINKFLAGS	= -Ttext 0xa0000000
 #HOSTCC		= gcc
 #
-# KERNELBASE isn't quite useless, but I need it to work
+# KERNELBASE is quite useless, but I need it to work
 # around a hardware bug in my Wreckstation board. Other people
 # would burn that @#!%# thing...
 #
 CC		= mips-linux-gcc -V 2.5.8 -D__KERNEL__ -I$(TOPDIR)/include
+CPP		= $(CC) -E $(CFLAGS)
 AR		= mips-linux-ar
 RANLIB		= mips-linux-ranlib
 STRIP		= mips-linux-strip
 
 CFLAGS := $(CFLAGS) #-pipe
 
-CFLAGS := $(CFLAGS) -Wa,-mips3 -mcpu=r4000 -D__R4000__ -DKERNELBASE=0xa0000000
+CFLAGS := $(CFLAGS) -DKERNELBASE=0xa0000000
+
+ifdef CONFIG_R4X00
+CFLAGS := $(CFLAGS) -Wa,-mips3 -mcpu=r4000 -D__R4000__
+endif
 
 HEAD := arch/mips/kernel/head.o
 
diff -u --recursive --new-file v1.1.85/linux/arch/mips/config.in linux/arch/mips/config.in
--- v1.1.85/linux/arch/mips/config.in	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/config.in	Wed Jan 25 08:54:22 1995
@@ -3,12 +3,20 @@
 # see the Configure script.
 #
 
+comment 'Machine setup'
+
+bool 'Support for Deskstation Tyne' CONFIG_DESKSTATION_TYNE y
+bool 'Support for Acer PICA 1 chipset' CONFIG_ACER_PICA_61 y
+bool 'Support for DECstation' CONFIG_DECSTATION n
+
+bool 'Generate code for R4x00' CONFIG_R4X00 y
+
 comment 'General setup'
 
-bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD n
-bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n
+bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
 bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y
 if [ "$CONFIG_ST506" = "y" ]; then
+  comment 'Please see block/drivers/README.ide for help/info on IDE drives'
   bool '   Use old (reliable) disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y
   if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
     bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
@@ -27,7 +35,7 @@
 
 if [ "$CONFIG_NET" = "y" ]; then
 comment 'Networking options'
-bool 'TCP/IP networking' CONFIG_INET n
+bool 'TCP/IP networking' CONFIG_INET y
 if [ "$CONFIG_INET" "=" "y" ]; then
 bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD y
 bool 'IP multicasting (ALPHA)' CONFIG_IP_MULTICAST n
@@ -89,7 +97,7 @@
 
 comment 'Network device support'
 
-bool 'Network device support?' CONFIG_NETDEVICES y
+bool 'Network device support?' CONFIG_NETDEVICES n
 if [ "$CONFIG_NETDEVICES" = "n" ]; then
 
 comment 'Skipping network driver configuration options...'
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/Makefile linux/arch/mips/kernel/Makefile
--- v1.1.85/linux/arch/mips/kernel/Makefile	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/Makefile	Wed Jan 25 08:54:22 1995
@@ -19,19 +19,27 @@
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
 OBJS  = process.o signal.o entry.o traps.o irq.o ptrace.o cache.o resume.o \
-	ioport.o setup.o bios32.o
+	ioport.o setup.o bios32.o tynedma.o
 
 all: kernel.o head.o
 
-head.o: head.s
-
-head.s: head.S $(TOPDIR)/include/linux/tasks.h
-
 cache.o: cache.s
 
 cache.s: cache.S $(TOPDIR)/include/asm/mipsconfig.h \
 	$(TOPDIR)/include/asm/regdef.h $(TOPDIR)/include/asm/segment.h
 
+entry.o: entry.s
+
+entry.s: entry.S $(TOPDIR)/include/linux/sys.h \
+	$(TOPDIR)/include/linux/autoconf.h $(TOPDIR)/include/asm/segment.h \
+	$(TOPDIR)/include/asm/mipsregs.h $(TOPDIR)/include/asm/mipsconfig.h \
+	$(TOPDIR)/include/asm/page.h $(TOPDIR)/include/asm/stackframe.h \
+	$(TOPDIR)/include/asm/regdef.h $(TOPDIR)/include/asm/processor.h
+
+head.o: head.s
+
+head.s: head.S $(TOPDIR)/include/linux/tasks.h
+
 resume.o: resume.s
 
 resume.s: resume.S $(TOPDIR)/include/asm/regdef.h \
@@ -48,7 +56,7 @@
 	sync
 
 dep:
-	$(CPP) $(CFLAGS) -M *.c > .depend
+	$(CPP) -M *.c > .depend
 
 modules:
 
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/entry.S linux/arch/mips/kernel/entry.S
--- v1.1.85/linux/arch/mips/kernel/entry.S	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/entry.S	Wed Jan 25 08:54:22 1995
@@ -12,10 +12,11 @@
  */
 
 #include <linux/sys.h>
+#include <linux/autoconf.h>
 #include <asm/segment.h>
 #include <asm/mipsregs.h>
 #include <asm/mipsconfig.h>
-#include <asm/mm.h>
+#include <asm/page.h>
 #include <asm/stackframe.h>
 #include <asm/regdef.h>
 #include <asm/processor.h>
@@ -185,16 +186,17 @@
 return:		RESTORE_ALL
 		.set	at
 
+#ifdef CONFIG_DESKSTATION_TYNE
 /*
- * Assumptions for _handle_int:
- *  - only bank a or b are possible interrupt sources
+ * Deskstation Tyne interrupt handler
  */
 		.text
 		.set	noreorder
 		.set	noat
-		.globl	_handle_int
+		.globl	_deskstation_tyne_handle_int
 		.align	5
-_handle_int:	SAVE_ALL
+_deskstation_tyne_handle_int:
+		SAVE_ALL
 		.set	at
 		CLI
 		lui	s0,%hi(PORT_BASE)
@@ -202,7 +204,7 @@
 		sb	t1,%lo(PORT_BASE+0x20)(s0)	# poll command
 		lb	t1,%lo(PORT_BASE+0x20)(s0)	# read result
 		li	s1,1
-		bgtz	t1,poll_second
+		bgtz	t1,Lpoll_second
 		andi	t1,t1,7
 		/*
 		 * Acknowledge first pic
@@ -243,11 +245,11 @@
 		sb	t1,%lo(PORT_BASE+0x21)(s0)	# delay slot
 
 		.align	5
-poll_second:	li	t1,0x0f
+Lpoll_second:	li	t1,0x0f
 		sb	t1,%lo(PORT_BASE+0xa0)(s0)	# poll command
 		lb	t1,%lo(PORT_BASE+0xa0)(s0)	# read result
 		lui	s4,%hi(_cache_A1)
-		bgtz	t1,spurious_interrupt
+		bgtz	t1,Lspurious_interrupt
 		andi	t1,t1,7
 		/*
 		 * Acknowledge second pic
@@ -288,7 +290,7 @@
 		sb	t1,%lo(PORT_BASE+0xa1)(s0)	# delay slot
 
 		.align	5
-spurious_interrupt:
+Lspurious_interrupt:
 		/*
 		 * Nothing happened... (whistle)
 		 */
@@ -298,7 +300,29 @@
 		addiu	t0,t0,1
 		jr	ra
 		sw	t0,%lo(_spurious_count)(t1)
+#endif /* CONFIG_DESKSTATION_TYNE */
 		
+#ifdef CONFIG_ACER_PICA_61
+/*
+ * Acer PICA interrupt handler dummy
+ */
+		.set	noreorder
+		.set	noat
+		.globl	_acer_pica_61_handle_int
+		.align	5
+_acer_pica_61_handle_int:
+		la	a0,acer_text
+		jal	_panic
+		nop
+1:		b	1b
+		nop
+acer_text:	.asciz	"Interrupt handler for Acer PICA not written yet"
+		.align	2
+#endif /* CONFIG_ACER_PICA_61 */
+
+		.text
+		.set	noreorder
+		.set	at
 		.globl	_interrupt
 		.align	5
 _interrupt:	move	s2,ra
@@ -418,13 +442,13 @@
 		ori	k0,k0,3
 		xori	k0,k0,3
 		lw	k1,(k0)
-		andi	k1,k1,PAGE_PRESENT
+		andi	k1,k1,_PAGE_PRESENT
 		beqz	k1,nopage_tlbl
 		/*
 		 * Present bit is set -> set valid and accessed bits
 		 */
 		lw	k1,(k0)				# delay slot
-		ori	k1,k1,PAGE_ACCESSED
+		ori	k1,k1,_PAGE_ACCESSED
 		sw	k1,(k0)
 		eret
 
@@ -483,13 +507,13 @@
 		ori	k0,k0,3
 		xori	k0,k0,3
 		lw	k1,(k0)
-		andi	k1,k1,(PAGE_PRESENT|PAGE_RW)
+		andi	k1,k1,(_PAGE_PRESENT|_PAGE_RW)
 		beqz	k1,nopage_tlbs
 		/*
 		 * Present and writable bits set -> set accessed and dirty bits.
 		 */
 		lw	k1,(k0)				# delay slot
-		ori	k1,k1,PAGE_ACCESSED|PAGE_DIRTY
+		ori	k1,k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
 		sw	k1,(k0)
 		/*
 		 * Now reload the entry into the tlb
@@ -567,13 +591,13 @@
 		ori	k0,k0,3
 		xori	k0,k0,3
 		lw	k1,(k0)
-		andi	k1,k1,PAGE_RW
+		andi	k1,k1,_PAGE_RW
 		beqz	k1,nopage_tlbs
 		/*
 		 * Present and writable bits set -> set accessed and dirty bits.
 		 */
 		lw	k1,(k0)				# delay slot
-		ori	k1,k1,(PAGE_ACCESSED|PAGE_DIRTY)
+		ori	k1,k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
 		sw	k1,(k0)
 		/*
 		 * Now reload the entry into the tlb
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/head.S linux/arch/mips/kernel/head.S
--- v1.1.85/linux/arch/mips/kernel/head.S	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/head.S	Wed Jan 25 08:54:22 1995
@@ -36,8 +36,6 @@
 		.globl	_empty_bad_page_table
 		.globl	_pg0
 		.globl	_empty_zero_page
-		.globl	_tmp_floppy_area
-		.globl	_floppy_track_buffer
 		.globl	_swapper_pg_dir
 
 		.text
@@ -438,16 +436,7 @@
 
 		.data
 /*
- * Instead of Intel's strange and unportable segment descriptor magic
- * we difference user and kernel space by their address.
- * Kernel space (== physical memory) is mapped at KSEG[01],
- * User space is mapped at 0x0.
- */
-		.globl	_segment_fs
-_segment_fs:	.word	KERNEL_DS
-
-/*
- * Initial mapping tables for supported Mips boards.
+ * Inital mapping tables for supported Mips boards.
  * First item is always the number of wired TLB entries,
  * following by EntryHi/EntryLo pairs and page mask.
  * Since everything must be quad-aligned (8) we insert
@@ -723,19 +712,25 @@
 
 			.org	0x6000
 
+#if defined (CONFIG_DESKSTATION_TYNE) && !defined (CONFIG_ACER_PICA_61)
+#if 0
 /*
  * tmp_floppy_area is used by the floppy-driver when DMA cannot
  * reach to a buffer-block. It needs to be aligned, so that it isn't
  * on a 64kB border.
  */
+			.globl	_tmp_floppy_area
 _tmp_floppy_area:	.fill	1024,1,0
+#endif
 /*
  * floppy_track_buffer is used to buffer one track of floppy data: it
  * has to be separate from the tmp_floppy area, as otherwise a single-
  * sector read/write can mess it up. It can contain one full cylinder (sic) of
  * data (36*2*512 bytes).
  */
+			.globl	_floppy_track_buffer
 _floppy_track_buffer:	.fill	512*2*36,1,0
+#endif /* defined (CONFIG_DESKSTATION_TYNE) && !defined (CONFIG_ACER_PICA_61) */
 			.globl	_kernelsp
 _kernelsp:		.word	0
 beepflag:		.word	0
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/irq.c linux/arch/mips/kernel/irq.c
--- v1.1.85/linux/arch/mips/kernel/irq.c	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/irq.c	Wed Jan 25 08:54:22 1995
@@ -15,6 +15,13 @@
  * Naturally it's not a 1:1 relation, but there are similarities.
  */
 
+/*
+ * The Deskstation Tyne is almost completly like an IBM compatible PC with
+ * another type of microprocessor. Therefore this code is almost completly
+ * the same. More work needs to be done to support Acer PICA and other
+ * machines.
+ */
+
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/process.c linux/arch/mips/kernel/process.c
--- v1.1.85/linux/arch/mips/kernel/process.c	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/process.c	Wed Jan 25 08:54:22 1995
@@ -44,7 +44,7 @@
 #if 0
 	/* Map out the low memory: it's no longer needed */
 	for (i = 0 ; i < 512 ; i++)
-		swapper_pg_dir[i] = 0;
+		pgd_clear(swapper_pg_dir + i);
 #endif
 
 	/* endless idle loop with no priority at all */
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/ptrace.c linux/arch/mips/kernel/ptrace.c
--- v1.1.85/linux/arch/mips/kernel/ptrace.c	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/ptrace.c	Wed Jan 25 08:54:22 1995
@@ -84,23 +84,30 @@
  */
 static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
 {
+	pgd_t * pgdir;
+	pte_t * pgtable;
 	unsigned long page;
 
 repeat:
-	page = *PAGE_DIR_OFFSET(vma->vm_task, addr);
-	if (page & PAGE_PRESENT) {
-		page &= PAGE_MASK;
-		page += PAGE_PTR(addr);
-		page = *((unsigned long *) page);
+	pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr);
+	if (pgd_none(*pgdir)) {
+		do_no_page(vma, addr, 0);
+		goto repeat;
+	}
+	if (pgd_bad(*pgdir)) {
+		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+		pgd_clear(pgdir);
+		return 0;
 	}
-	if (!(page & PAGE_PRESENT)) {
+	pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
+	if (!pte_present(*pgtable)) {
 		do_no_page(vma, addr, 0);
 		goto repeat;
 	}
+	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	if (page >= high_memory)
 		return 0;
-	page &= PAGE_MASK;
 	page += addr & ~PAGE_MASK;
 	return *(unsigned long *) page;
 }
@@ -117,39 +124,40 @@
 static void put_long(struct vm_area_struct * vma, unsigned long addr,
 	unsigned long data)
 {
-	unsigned long page, pte = 0;
-	int readonly = 0;
+	pgd_t *pgdir;
+	pte_t *pgtable;
+	unsigned long page;
 
 repeat:
-	page = *PAGE_DIR_OFFSET(vma->vm_task, addr);
-	if (page & PAGE_PRESENT) {
-		page &= PAGE_MASK;
-		page += PAGE_PTR(addr);
-		pte = page;
-		page = *((unsigned long *) page);
+	pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr);
+	if (!pgd_present(*pgdir)) {
+		do_no_page(vma, addr, 1);
+		goto repeat;
 	}
-	if (!(page & PAGE_PRESENT)) {
-		do_no_page(vma, addr, 0 /* PAGE_RW */);
+	if (pgd_bad(*pgdir)) {
+		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+		pgd_clear(pgdir);
+		return;
+	}
+	pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
+	if (!pte_present(*pgtable)) {
+		do_no_page(vma, addr, 1);
 		goto repeat;
 	}
-	if (!(page & PAGE_RW)) {
-		if (!(page & PAGE_COW))
-			readonly = 1;
-		do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT);
+	page = pte_page(*pgtable);
+	if (!pte_write(*pgtable)) {
+		do_wp_page(vma, addr, 1);
 		goto repeat;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
-	if (page >= high_memory)
-		return;
+	if (page < high_memory) {
+		page += addr & ~PAGE_MASK;
+		*(unsigned long *) page = data;
+	}
 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
-	*(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW);
-	page &= PAGE_MASK;
-	page += addr & ~PAGE_MASK;
-	*(unsigned long *) page = data;
-	if (readonly) {
-		*(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
-		invalidate();
-	} 
+/* this should also re-instate whatever read-only mode there was before */
+	*pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
+	invalidate();
 }
 
 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/resume.S linux/arch/mips/kernel/resume.S
--- v1.1.85/linux/arch/mips/kernel/resume.S	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/resume.S	Wed Jan 25 08:54:22 1995
@@ -59,14 +59,7 @@
 		 */
 		ori	t2,t1,0x1f
 		xori	t2,0x1e
-
-		/*
-		 * Save fs (may be pointing to kernel memory)
-		 */
-		lui	t4,%hi(_segment_fs)
-		lw	t3,%lo(_segment_fs)(t4)
 		mtc0	t2,CP0_STATUS
-		sw	t3,TOFF_FS(t0)
 
 		/*
 		 * Save non-scratch registers
@@ -248,14 +241,10 @@
 		lw	ra,TOFF_REG31(a0)
 
 		/*
-		 * Restore fs segment pointer
 		 * Restore status register
 		 */
-		lw	t0,TOFF_FS(a0)
-		lw	t1,TOFF_KSP(a0)
-		sw	t0,%lo(_segment_fs)(t4)
-		sw	t1,_kernelsp
+		lw	t0,TOFF_KSP(a0)
+		sw	t0,_kernelsp
 
 		jr	ra
 		mtc0	a2,CP0_STATUS			# delay slot
-
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
--- v1.1.85/linux/arch/mips/kernel/traps.c	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/kernel/traps.c	Wed Jan 25 08:54:22 1995
@@ -34,21 +34,16 @@
 
 #define get_seg_byte(seg,addr) ({ \
 register unsigned char __res; \
-int unsigned long save; \
-save = segment_fs; \
 __res = get_user_byte(addr); \
-segment_fs = save; \
 __res;})
 
 #define get_seg_long(seg,addr) ({ \
 register unsigned long __res; \
-int unsigned long save; \
-save = segment_fs; \
 __res = get_user_word(addr); \
-segment_fs = save; \
 __res;})
 
-extern asmlinkage void handle_int(void);
+extern asmlinkage void deskstation_tyne_handle_int(void);
+extern asmlinkage void acer_pica_61_handle_int(void);
 extern asmlinkage void handle_mod(void);
 extern asmlinkage void handle_tlbl(void);
 extern asmlinkage void handle_tlbs(void);
@@ -373,6 +368,12 @@
 	 */
 	switch(boot_info.machtype) {
 	case MACH_DESKSTATION_TYNE:
-		set_except_vector(0, handle_int);
+		set_except_vector(0, deskstation_tyne_handle_int);
+		break;
+	case MACH_ACER_PICA_61:
+		set_except_vector(0, acer_pica_61_handle_int);
+		break;
+	default:
+		panic("Unknown machine type");
 		}
 }
diff -u --recursive --new-file v1.1.85/linux/arch/mips/kernel/tynedma.c linux/arch/mips/kernel/tynedma.c
--- v1.1.85/linux/arch/mips/kernel/tynedma.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/mips/kernel/tynedma.c	Wed Jan 25 08:54:22 1995
@@ -0,0 +1,35 @@
+/*
+ * Tiny Tyne DMA buffer allocator
+ *
+ * Copyright (C) 1995 Ralf Baechle
+ */
+#include <linux/autoconf.h>
+#include <linux/types.h>
+#include <asm/bootinfo.h>
+
+#ifdef CONFIG_DESKSTATION_TYNE
+
+static unsigned long allocated;
+
+/*
+ * Not very sophisticated, but should suffice for now...
+ */
+unsigned long deskstation_tyne_dma_alloc(size_t size)
+{
+	unsigned long ret = allocated;
+	allocated += size;
+	if (allocated > boot_info.dma_cache_size)
+		ret = -1;
+	return ret;
+}
+
+void deskstation_tyne_dma_init(void)
+{
+	if (boot_info.machtype != MACH_DESKSTATION_TYNE)
+		return;
+	allocated = 0;
+	printk ("Deskstation Tyne DMA (%luk) buffer initialized.\n",
+	        boot_info.dma_cache_size >> 10);
+}
+
+#endif /* CONFIG_DESKSTATION_TYNE */
diff -u --recursive --new-file v1.1.85/linux/arch/mips/mm/Makefile linux/arch/mips/mm/Makefile
--- v1.1.85/linux/arch/mips/mm/Makefile	Sun Jan 22 21:39:38 1995
+++ linux/arch/mips/mm/Makefile	Wed Jan 25 08:54:22 1995
@@ -1,5 +1,5 @@
 #
-# Makefile for the linux i386-specific parts of the memory manager.
+# Makefile for the linux mips-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -22,7 +22,7 @@
 modules:
 
 dep:
-	$(CPP) $(CFLAGS) -M *.c > .depend
+	$(CPP) -M *.c > .depend
 
 #
 # include a dependency file if one exists
diff -u --recursive --new-file v1.1.85/linux/arch/mips/mm/fault.c linux/arch/mips/mm/fault.c
--- v1.1.85/linux/arch/mips/mm/fault.c	Thu Jan 26 07:49:03 1995
+++ linux/arch/mips/mm/fault.c	Wed Jan 25 08:54:22 1995
@@ -27,6 +27,12 @@
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
  * routines.
+ *
+ * The error_code parameter just the same as in the i386 version:
+ *
+ *	bit 0 == 0 means no page found, 1 means protection fault
+ *	bit 1 == 0 means read, 1 means write
+ *	bit 2 == 0 means kernel, 1 means user-mode
  */
 asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
@@ -38,9 +44,12 @@
 	__asm__("dmfc0\t%0,$8"
 	        : "=r" (address));
 
-	vma = find_vma(current, address);
-	if (!vma)
-		goto bad_area;
+	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			goto bad_area;
+		if (vma->vm_end > address)
+			break;
+	}
 	if (vma->vm_start <= address)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
@@ -54,23 +63,24 @@
  * we can handle it..
  */
 good_area:
-#if 0
-	if (regs->eflags & VM_MASK) {
-		unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
-		if (bit < 32)
-			current->tss.screen_bitmap |= 1 << bit;
-	}
-#endif
-	if (!(vma->vm_page_prot & PAGE_USER))
-		goto bad_area;
-	if (error_code & PAGE_PRESENT) {
-		if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW)))
+	/*
+	 * was it a write?
+	 */
+	if (error_code & 2) {
+		if (!(vma->vm_flags & VM_WRITE))
 			goto bad_area;
-		do_wp_page(vma, address, error_code);
+	} else {
+		/* read with protection fault? */
+		if (error_code & 1)
+			goto bad_area;
+		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+			goto bad_area;
+	}
+	if (error_code & 1) {
+		do_wp_page(vma, address, error_code & 2);
 		return;
 	}
-printk("do_page_fault: do_no_page(%x, %x, %d)", vma, address, error_code);
-	do_no_page(vma, address, error_code);
+	do_no_page(vma, address, error_code & 2);
 	return;
 
 /*
@@ -78,30 +88,30 @@
  * Fix it, but check if it's kernel or user first..
  */
 bad_area:
-printk("Bad Area...\n");
-	if (error_code & PAGE_USER) {
+	if (user_mode(regs)) {
 		current->tss.cp0_badvaddr = address;
 		current->tss.error_code = error_code;
+#if 0
 		current->tss.trap_no = 14;
+#endif
 		send_sig(SIGSEGV, current, 1);
 		return;
 	}
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- */
-	printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
+	/*
+	 * Oops. The kernel tried to access some bad page. We'll have to
+	 * terminate things with extreme prejudice.
+	 */
 	if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) {
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
-		pg0[0] = PAGE_SHARED;
+		pg0[0] = pte_val(mk_pte(0, PAGE_SHARED));
 	} else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n",address);
 	page = current->tss.pg_dir;
 	printk(KERN_ALERT "current->tss.pg_dir = %08lx\n", page);
-	page = ((unsigned long *) page)[address >> 22];
+	page = ((unsigned long *) page)[address >> PGDIR_SHIFT];
 	printk(KERN_ALERT "*pde = %08lx\n", page);
-	if (page & PAGE_PRESENT) {
+	if (page & 1) {
 		page &= PAGE_MASK;
 		address &= 0x003ff000;
 		page = ((unsigned long *) page)[address >> PAGE_SHIFT];
diff -u --recursive --new-file v1.1.85/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c
--- v1.1.85/linux/arch/mips/mm/init.c	Thu Jan 26 07:49:03 1995
+++ linux/arch/mips/mm/init.c	Wed Jan 25 08:54:22 1995
@@ -23,6 +23,7 @@
 
 extern unsigned long pg0[1024];		/* page table for 0-4MB for everybody */
 
+extern void deskstation_tyne_dma_init(void);
 extern void scsi_mem_init(unsigned long);
 extern void sound_mem_init(void);
 extern void die_if_kernel(char *,struct pt_regs *,long);
@@ -41,7 +42,7 @@
  * ZERO_PAGE is a special page that is used for zero-initialized
  * data and COW.
  */
-unsigned long __bad_pagetable(void)
+pte_t * __bad_pagetable(void)
 {
 	extern char empty_bad_page_table[PAGE_SIZE];
 	unsigned long dummy;
@@ -55,14 +56,14 @@
 		".set\treorder"
 		:"=r" (dummy),
 		 "=r" (dummy)
-		:"r" (BAD_PAGE + PAGE_TABLE),
+		:"r" (pte_val(BAD_PAGE)),
 		 "0" ((long) empty_bad_page_table),
 		 "1" (PTRS_PER_PAGE));
 
-	return (unsigned long) empty_bad_page_table;
+	return (pte_t *) empty_bad_page_table;
 }
 
-unsigned long __bad_page(void)
+pte_t __bad_page(void)
 {
 	extern char empty_bad_page[PAGE_SIZE];
 	unsigned long dummy;
@@ -79,7 +80,7 @@
 		:"0" ((long) empty_bad_page),
 		 "1" (PTRS_PER_PAGE));
 
-	return (unsigned long) empty_bad_page;
+	return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED));
 }
 
 unsigned long __zero_page(void)
@@ -141,8 +142,8 @@
  */
 unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
 {
-	unsigned long * pg_dir;
-	unsigned long * pg_table;
+	pgd_t * pg_dir;
+	pte_t * pg_table;
 	unsigned long tmp;
 	unsigned long address;
 
@@ -150,23 +151,21 @@
 	address = 0;
 	pg_dir = swapper_pg_dir;
 	while (address < end_mem) {
-		tmp = *pg_dir;
-		tmp &= PAGE_MASK;
-		if (!tmp) {
-			tmp = start_mem;
+		if (pgd_none(pg_dir[0])) {
+			pgd_set(pg_dir, (pte_t *) start_mem);
 			start_mem += PAGE_SIZE;
 		}
 		/*
 		 * also map it in at 0x00000000 for init
 		 */
-		*pg_dir = tmp | PAGE_TABLE;
+		pg_table = (pte_t *) pgd_page(pg_dir[0]);
+		pgd_set(pg_dir, pg_table);
 		pg_dir++;
-		pg_table = (unsigned long *) (tmp & PAGE_MASK);
 		for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) {
 			if (address < end_mem)
-				*pg_table = address | PAGE_SHARED;
+				*pg_table = mk_pte(address, PAGE_SHARED);
 			else
-				*pg_table = 0;
+				pte_clear(pg_table);
 			address += PAGE_SIZE;
 		}
 	}
@@ -195,6 +194,9 @@
 		mem_map[MAP_NR(start_mem)] = 0;
 		start_mem += PAGE_SIZE;
 	}
+#ifdef CONFIG_DESKSTATION_TYNE
+	deskstation_tyne_dma_init();
+#endif
 #ifdef CONFIG_SCSI
 	scsi_mem_init(high_memory);
 #endif
@@ -225,6 +227,7 @@
 		codepages << (PAGE_SHIFT-10),
 		reservedpages << (PAGE_SHIFT-10),
 		datapages << (PAGE_SHIFT-10));
+	pg0[0] = pte_val(mk_pte(0, PAGE_READONLY));
 
 	invalidate();
 	return;
diff -u --recursive --new-file v1.1.85/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.1.85/linux/drivers/block/README.ide	Sun Jan 22 23:03:58 1995
+++ linux/drivers/block/README.ide	Thu Jan 26 07:25:25 1995
@@ -128,7 +128,8 @@
 (8 bits for sector, 16 bits for cylinder, and 4 bits for head) for addressing
 individual disk sectors of 512 bytes each (in "Linear Block Address" (LBA)
 mode, there is still only a total of 28 bits available in the hardware).
-This "limits" the capacity of an IDE drive to no more than 128GB (Giga-bytes).  All current day IDE drives are somewhat smaller than this upper limit, and
+This "limits" the capacity of an IDE drive to no more than 128GB (Giga-bytes).
+All current day IDE drives are somewhat smaller than this upper limit, and
 within a few years, ATAPI disk drives will raise the limit considerably.
 
 All IDE disk drives "suffer" from a "16-heads" limitation:  the hardware has
@@ -229,7 +230,7 @@
 	A danger with this approach is that whenever an MS-DOS "defragmentation"
 	program is run (like Norton "speeddisk"), it may move the Linux boot
 	files around, confusing LILO and making the (Linux) system unbootable.
-	Be sure to keep a "boot floppy" kernel at hand for such circumstances.
+	Be sure to keep a kernel "boot floppy" at hand for such circumstances.
 
 If you "don't do DOS", then partition as you please, but remember to create
 a small partition to hold the /boot directory (and vmlinuz) as described above
diff -u --recursive --new-file v1.1.85/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.1.85/linux/drivers/block/ide.c	Sun Jan 22 23:03:58 1995
+++ linux/drivers/block/ide.c	Thu Jan 26 07:25:25 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 3.10  January 21, 1995
+ *  linux/drivers/block/ide.c	Version 3.11  January 25, 1995
  *
  *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
  */
@@ -100,6 +100,7 @@
  *			fix probing for old Seagates without HD_ALTSTATUS
  *			fix byte-ordering for some NEC cdrom drives
  *  Version 3.10	disable multiple mode by default; was causing trouble
+ *  Version 3.11	fix mis-identification of old WD disks as cdroms
  *
  *  To do:
  *	- special 32-bit controller-type detection & support
@@ -1597,10 +1598,9 @@
 
 static unsigned long probe_mem_start;	/* used by drive/irq probing routines */
 
-static void do_identify (ide_dev_t *dev)
+static void do_identify (ide_dev_t *dev, byte cmd)
 {
 	int bswap;
-	unsigned short model01;
 	struct hd_driveid *id;
 	unsigned long capacity, check;
 
@@ -1610,24 +1610,10 @@
 	sti();
 
 	/*
-	 * Non-ATAPI drives seem to always use big-endian string ordering.
-	 * Most ATAPI cdrom drives, such as the Vertos, and some NEC and Mitsumi
-	 * models, use little-endian.  But some NEC/Mitsumi revisions appear to
-	 * use big-endian, confusing the issue.  We try to take all of this
-	 * into consideration, "knowing" that Mitsumi drive model names begin
-	 * with "FX" and NEC drive model names begin with "NE".
+	 *  WIN_IDENTIFY returns little-endian info,
+	 *  WIN_PIDENTIFY return big-endian info.
 	 */
-#define PAIR(hi,lo)	((unsigned short)((hi<<8)|(lo&0xff)))
-	model01 = PAIR(id->model[0],id->model[1]);
-	if (model01 == PAIR('N','E') || model01 == PAIR('F','X'))
-		bswap = 0;	/* little endian NEC or Mitsumi */
-	else if (model01 == PAIR('E','N') || model01 == PAIR('X','F'))
-		bswap = 1;	/* big endian NEC or Mitsumi */
-	else if ((id->config & 0x8000) || dev->type == cdrom)
-		bswap = 0;	/* all other ATAPI drives */
-	else
-		bswap = 1;	/* all other non-ATAPI drives */
-#undef PAIR
+	bswap = (cmd == WIN_IDENTIFY);
 	fixstring (id->model,     sizeof(id->model),     bswap);
 	fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
 	fixstring (id->serial_no, sizeof(id->serial_no), bswap);
@@ -1635,7 +1621,7 @@
 	/*
 	 * Check for an ATAPI device
 	 */
-	if (id->config & 0x8000) {
+	if (cmd == WIN_PIDENTIFY) {
 #ifdef CONFIG_BLK_DEV_IDECD
 		byte type = (id->config >> 8) & 0x0f;
 #endif	/* CONFIG_BLK_DEV_IDECD */
@@ -1781,7 +1767,7 @@
 	delay_10ms();		/* wait for IRQ and DRQ_STAT */
 	if (OK_STAT(GET_STAT(DEV_HWIF),DRQ_STAT,BAD_RW_STAT)) {
 		cli();			/* some systems need this */
-		do_identify(dev);	/* drive returned ID */
+		do_identify(dev, cmd);	/* drive returned ID */
 		rc = 0;			/* success */
 	} else
 		rc = 2;			/* drive refused ID */
diff -u --recursive --new-file v1.1.85/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.1.85/linux/drivers/char/Makefile	Thu Jan 26 07:49:04 1995
+++ linux/drivers/char/Makefile	Tue Jan 24 14:17:57 1995
@@ -94,7 +94,9 @@
 
 dep:
 	$(CPP) -M $(SRCS) > .depend
+#ifdef MODULES
 	$(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+#endif
 
 dummy:
 
diff -u --recursive --new-file v1.1.85/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.1.85/linux/drivers/char/console.c	Thu Jan 26 07:49:04 1995
+++ linux/drivers/char/console.c	Fri Jan 27 11:13:35 1995
@@ -69,6 +69,13 @@
 #define BLANK 0x0020
 #define CAN_LOAD_EGA_FONTS    /* undefine if the user must not do this */
 
+/* A bitmap for codes <32. A bit of 1 indicates that the code
+ * corresponding to that bit number invokes some special action
+ * (such as cursor movement) and should not be displayed as a
+ * glyph unless the disp_ctrl mode is explicitly enabled.
+ */
+#define CTRL_ACTION 0xd00ff80
+
 /*
  *  NOTE!!! We sometimes disable and enable interrupts for a short while
  * (to put a word in video IO), but this will work even for keyboard
@@ -1143,6 +1150,9 @@
 				report_mouse = on_off ? 2 : 0;
 				break;
 		} else switch(par[i]) {		/* ANSI modes set/reset */
+			case 3:			/* Monitor (display ctrls) */
+				disp_ctrl = on_off;
+				break;
 			case 4:			/* Insert Mode on/off */
 				decim = on_off;
 				break;
@@ -1312,7 +1322,7 @@
 	utf             = 0;
 	utf_count       = 0;
 
-	disp_ctrl	= 1;
+	disp_ctrl	= 0;
 	toggle_meta	= 0;
 
 	decscnm		= 0;
@@ -1445,9 +1455,15 @@
 		    ok = 0;
 		}
 
-		/* Can print ibm (even if 0), and latin1 provided
-		   it is a printing char or control chars are printed ^@ */
-		if (!ok && tc && (c >= 32 || (disp_ctrl && (c&0x7f) != 27)))
+		/* If the original code was < 32 we only allow a
+		 * glyph to be displayed if the code is not normally
+		 * used (such as for cursor movement) or if the
+		 * disp_ctrl mode has been explicitly enabled.
+		 * Note: ESC is *never* allowed to be displayed as
+		 * that would disable all escape sequences!
+		 */
+		if (!ok && tc && (c >= 32 || (disp_ctrl && c != 0x1b)
+		|| !((CTRL_ACTION >> c) & 1)))
 		    ok = 1;
 
 		if (vc_state == ESnormal && ok) {
@@ -1497,10 +1513,12 @@
 			case 14:
 				charset = 1;
 				translate = G1_charset;
+				disp_ctrl = 1;
 				continue;
 			case 15:
 				charset = 0;
 				translate = G0_charset;
+				disp_ctrl = 0;
 				continue;
 			case 24: case 26:
 				vc_state = ESnormal;
diff -u --recursive --new-file v1.1.85/linux/drivers/net/CONFIG linux/drivers/net/CONFIG
--- v1.1.85/linux/drivers/net/CONFIG	Wed Nov  2 11:38:35 1994
+++ linux/drivers/net/CONFIG	Thu Jan 26 06:53:58 1995
@@ -39,6 +39,10 @@
 #  EWRK3		The DIGITAL series of AT Ethernet Cards (DE203/4/5)
 #       EWRK3_DEBUG	Set the desired debug level
 #
+#  DE4x5		The DIGITAL series of PCI/EISA Ethernet Cards,
+#			DE425, DE434 and DE435
+#       DE4x5_DEBUG	Set the desired debug level
+#
 
 # The following options exist, but cannot be set in this file.
 #  lance.c
@@ -60,3 +64,4 @@
 PLIP_OPTS	=
 DEPCA_OPTS	= -DDEPCA_DEBUG=1
 EWRK3_OPTS	= -DEWRK3_DEBUG=1
+DE4x5_OPTS	= -DDE4x5_DEBUG=1
diff -u --recursive --new-file v1.1.85/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.1.85/linux/drivers/net/Makefile	Thu Jan 26 07:49:05 1995
+++ linux/drivers/net/Makefile	Thu Jan 26 06:55:24 1995
@@ -188,6 +188,12 @@
 NETDRV_OBJS := $(NETDRV_OBJS) atp.o
 endif
 
+ifdef CONFIG_DE4x5
+NETDRV_OBJS := $(NETDRV_OBJS) de4x5.o
+de4x5.o: de4x5.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(DE4x5_OPTS) -c $<
+endif
+
 ifdef CONFIG_NI52
 NETDRV_OBJS := $(NETDRV_OBJS) ni52.o
 endif
diff -u --recursive --new-file v1.1.85/linux/drivers/net/README.de4x5 linux/drivers/net/README.de4x5
--- v1.1.85/linux/drivers/net/README.de4x5	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/README.de4x5	Thu Jan 26 06:53:58 1995
@@ -0,0 +1,27 @@
+The  de425/de434/de435 driver in this  distribution is designed to work with
+the Digital Equipment Corporation series  of PCI/EISA ethernet cards (DE425,
+DE434, DE435) and with all kernels that support PCI.
+
+Auto media detection is provided so that the  media choice isn't compiled in
+and is flexible enough to be able to reconfigure on-the-fly.
+
+The ability to  load this  driver as  a loadable  module has been  included,
+although I don't recommend its use with PCI, since PCI dynamically allocates
+where the card will go at boot time.
+
+The performance we've achieved  so far has been  measured through the 'ttcp'
+tool at   1.08MB/s.  This measures   the total tcp stack  performance which
+includes  the  card,  so  don't expect   to  get  much  nearer the  1.25MB/s
+theoretical ethernet rate.
+
+    ************************************************************************
+    However there is still a known bug which causes ttcp to hang on transmit
+    (receive  is  OK), although  the  adapter/driver  continues to  function
+    normally for  other applications e.g.  nfs  mounting disks, pinging etc.
+    The cause is under investigation.
+    ************************************************************************
+
+
+Enjoy!
+
+Dave
diff -u --recursive --new-file v1.1.85/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.1.85/linux/drivers/net/Space.c	Fri Jan 13 10:12:22 1995
+++ linux/drivers/net/Space.c	Thu Jan 26 06:53:58 1995
@@ -49,6 +49,7 @@
 extern int depca_probe(struct device *);
 extern int apricot_probe(struct device *);
 extern int ewrk3_probe(struct device *);
+extern int de4x5_probe(struct device *);
 extern int el1_probe(struct device *);
 extern int el16_probe(struct device *);
 extern int elplus_probe(struct device *);
@@ -110,6 +111,9 @@
 #endif
 #ifdef CONFIG_EWRK3             /* DEC EtherWORKS 3 */
         && ewrk3_probe(dev)
+#endif
+#ifdef CONFIG_DE4X5             /* DEC DE425, DE434, DE435 adapters */
+        && de4x5_probe(dev)
 #endif
 #ifdef CONFIG_APRICOT		/* Apricot I82596 */
 	&& apricot_probe(dev)
diff -u --recursive --new-file v1.1.85/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v1.1.85/linux/drivers/net/de4x5.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/de4x5.c	Thu Jan 26 06:53:58 1995
@@ -0,0 +1,1973 @@
+/*  de4x5.c: A DIGITAL DE425/DE434/DE435 ethernet driver for linux.
+
+    Copyright 1994 Digital Equipment Corporation.
+
+    This software may be used and distributed according to the terms of
+    the GNU Public License, incorporated herein by reference.
+
+    This driver is written for the Digital Equipment Corporation series
+    of EtherWORKS ethernet cards:
+
+	DE425 TP/COAX EISA
+	DE434 TP PCI
+	DE435 TP/COAX/AUI PCI
+
+    The driver has been tested on a  relatively busy network using the DE425
+    and DE435 cards and benchmarked with 'ttcp': it  transferred 16M of data
+    at 1.08MB/s (8.6Mb/s) to a DECstation 5000/200.
+
+    ************************************************************************
+    However there is still a known bug which causes ttcp to hang on transmit
+    (receive  is  OK), although  the  adapter/driver  continues to  function
+    normally for  other applications e.g.  nfs  mounting disks, pinging etc.
+    The cause is under investigation.
+    ************************************************************************
+
+    The author may    be  reached as davies@wanton.lkg.dec.com  or   Digital
+    Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+    =========================================================================
+    This driver has been written  substantially  from scratch, although  its
+    inheritance of style and stack interface from 'ewrk3.c' and in turn from
+    Donald Becker's 'lance.c' should be obvious.
+
+    Upto 15 EISA cards can be supported under this driver, limited primarily
+    by the available IRQ lines.  I have  checked different configurations of
+    multiple depca, EtherWORKS 3 cards and de4x5 cards and  have not found a
+    problem yet (provided you have at least depca.c v0.38) ...
+
+    PCI support  has been added  to allow the  driver to work with the DE434
+    and  DE435 cards. The I/O  accesses  are a  bit of a   kludge due to the
+    differences  in the  EISA and PCI    CSR address offsets  from the  base
+    address.
+
+    The ability to load this driver  as a loadable  module has been included
+    and used  extensively during the  driver development (to save those long
+    reboot sequences).  I  don't  recommend using loadable drivers  with PCI
+    however,  since the  PCI BIOS allocates   the  I/O and  memory addresses
+    dynamically at  boot time.  To  utilise this  ability, you  have to do 8
+    things:
+
+    0) have a copy of the loadable modules code installed on your system.
+    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
+    temporary directory.
+    2) edit the  source code near  line 1945 to reflect  the I/O address and
+    IRQ you're using, or assign these when loading by:
+
+                   insmod de4x5.o irq=x io=y
+
+    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
+    that the correct bits are compiled (see end of source code).
+    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
+    kernel with the de4x5 configuration turned off and reboot.
+    5) insmod de4x5.o
+    6) run the net startup bits for your new eth?? interface manually 
+    (usually /etc/rc.inet[12] at boot time). 
+    7) enjoy!
+
+    Note that autoprobing is not allowed in loadable modules - the system is
+    already up and running and you're messing with interrupts.
+
+    To unload a module, turn off the associated interface 
+    'ifconfig eth?? down' then 'rmmod de4x5'.
+
+    Automedia detection is included so that in  principal you can disconnect
+    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a
+    pause whilst the   driver figures out   where its media went).  My tests
+    using ping showed that it appears to work....
+
+    TO DO:
+    ------
+      1.      Improve the timing loops to be accurate across different CPUs
+              and speeds.
+
+
+    Revision History
+    ----------------
+
+    Version   Date        Description
+  
+      0.1     17-Nov-94   Initial writing. ALPHA code release.
+      0.2     13-Jan-95   Added PCI support for DE435's
+      0.21    19-Jan-95   Added auto media detection
+
+    =========================================================================
+*/
+
+static char *version = "de4x5.c:v0.21 1/19/95 davies@wanton.lkg.dec.com\n";
+
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
+
+#include "de4x5.h"
+
+#ifdef DE4X5_DEBUG
+static int de4x5_debug = DE4X5_DEBUG;
+#else
+static int de4x5_debug = 1;
+#endif
+
+#ifndef PROBE_LENGTH
+#define PROBE_LENGTH    32
+#endif
+
+#define ETH_PROM_SIG "FF0055AAFF0055AA"
+
+#define DE4X5_SIGNATURE {"DE425",""}
+#define DE4X5_NAME_LENGTH 8
+
+#define DE4X5_EISA_IO_PORTS 0x0c00       /* I/O port base address, slot 0 */
+
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+#define DE4X5_EISA_SEARCH 0x00000001     /* probe search mask */
+static u_long eisa_slots_full =
+                       DE4X5_EISA_SEARCH;/* holds which EISA slots hold */
+				         /* DE425s, for multi-DE425 case */
+#define PCI_MAX_BUS_NUM 8
+static u_long pci_slots_full[PCI_MAX_BUS_NUM];  /* Which PCI slots used */
+                                         /* on up to PCI_MAX_BUS_NUM buses */
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL   /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL   /* Ethernet CRC, little endian */
+
+#define LWPAD ((long)(sizeof(long) - 1)) /* for longword alignment */
+
+/*
+** DE4X5 IRQ ENABLE/DISABLE
+*/
+static u_long irq_mask = IMR_RIM | IMR_TIM | IMR_TUM ;
+
+static u_long irq_en   = IMR_NIM | IMR_AIM;
+
+#define ENABLE_IRQs \
+  imr |= irq_en;\
+  outl(imr, DE4X5_IMR)                      /* Enable the IRQs */
+
+#define DISABLE_IRQs \
+  imr = inl(DE4X5_IMR);\
+  imr &= ~irq_en;\
+  outl(imr, DE4X5_IMR)                      /* Disable the IRQs */
+
+#define UNMASK_IRQs \
+  imr |= irq_mask;\
+  outl(imr, DE4X5_IMR)                      /* Unmask the IRQs */
+
+#define MASK_IRQs \
+  imr = inl(DE4X5_IMR);\
+  imr &= ~irq_mask;\
+  outl(imr, DE4X5_IMR)                      /* Mask the IRQs */
+
+/*
+** DE4X5 START/STOP
+*/
+#define START_DE4X5 \
+  omr = inl(DE4X5_OMR);\
+  omr |= OMR_ST | OMR_SR;\
+  outl(omr, DE4X5_OMR)                      /* Enable the TX and/or RX */
+
+#define STOP_DE4X5 \
+  omr = inl(DE4X5_OMR);\
+  omr &= ~(OMR_ST|OMR_SR);\
+  outl(omr, DE4X5_OMR)                      /* Disable the TX and/or RX */
+
+/*
+** DE4X5 SIA RESET
+*/
+#define RESET_SIA \
+  outl(SICR_RESET, DE4X5_SICR);             /* Reset SIA connectivity regs */ \
+  outl(STRR_RESET, DE4X5_STRR);             /* Write reset values */ \
+  outl(SIGR_RESET, DE4X5_SIGR)              /* Write reset values */
+
+/*
+** Ethernet Packet Info
+*/
+#define PKT_BUF_SZ	1544            /* Buffer size for each Tx/Rx buffer */
+#define MAX_PKT_SZ   	1514            /* Maximum ethernet packet length */
+#define MAX_DAT_SZ   	1500            /* Maximum ethernet data length */
+#define MIN_DAT_SZ   	1               /* Minimum ethernet data length */
+#define PKT_HDR_LEN     14              /* Addresses and data length info */
+
+/*
+** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous
+** and have sizes of both a power of 2 and a multiple of 4.
+** A size of 256 bytes for each buffer was chosen because over 90% of
+** all packets in our network are <256 bytes long.
+*/
+#define NUM_RX_DESC 64                       /* Number of RX descriptors */
+#define NUM_TX_DESC 8                        /* Number of TX descriptors */
+#define BUFF_ALLOC_RETRIES 10                /* In case of memory shortage */
+#define RX_BUFF_SZ 256                       /* Power of 2 for kmalloc and */
+                                             /* Multiple of 4 for DC21040 */
+
+struct de4x5_desc {
+    volatile long status;
+    u_long des1;
+    char *buf;
+    char *next;
+  };
+
+/*
+** The DE4X5 private structure
+*/
+#define DE4X5_PKT_STAT_SZ 16
+#define DE4X5_PKT_BIN_SZ  128                /* Should be >=100 unless you
+                                                increase DE4X5_PKT_STAT_SZ */
+
+struct de4x5_private {
+    struct de4x5_desc rx_ring[NUM_RX_DESC];  /* RX descriptor ring */
+    struct de4x5_desc tx_ring[NUM_TX_DESC];  /* TX descriptor ring */
+    struct sk_buff *skb[NUM_TX_DESC];        /* TX skb for freeing when sent */
+    int rx_new, rx_old;                      /* RX descriptor ring pointers */
+    int tx_new, tx_old;                      /* TX descriptor ring pointers */
+    char setup_frame[SETUP_FRAME_LEN];       /* Holds MCA and PA info. */
+    struct enet_statistics stats;            /* Public stats */
+    struct {
+      unsigned long bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+      unsigned long unicast;
+      unsigned long multicast;
+      unsigned long broadcast;
+      unsigned long excessive_collisions;
+      unsigned long tx_underruns;
+      unsigned long excessive_underruns;
+    } pktStats;
+    char rxRingSize;
+    char txRingSize;
+    char bus;                                /* EISA or PCI */
+    char lostMedia;                          /* Possibly lost media */
+};
+
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+			 lp->tx_old+lp->txRingSize-lp->tx_new-1:\
+                         lp->tx_old               -lp->tx_new-1)
+#define TX_SUSPENDED   (((sts & STS_TS) ^ TS_SUSP)==0)
+
+/*
+** Public Functions
+*/
+static int  de4x5_open(struct device *dev);
+static int  de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void de4x5_interrupt(int reg_ptr);
+static int  de4x5_close(struct device *dev);
+static struct enet_statistics *de4x5_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static int  de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int  de4x5_hw_init(struct device *dev, short iobase);
+static int  de4x5_init(struct device *dev);
+static int  de4x5_rx(struct device *dev);
+static int  de4x5_tx(struct device *dev);
+
+static int  autoconf_media(struct device *dev);
+static void create_packet(struct device *dev, char *frame, int len);
+static void load_packet(struct device *dev, char *buf, u_long flags, struct sk_buff *skb);
+static void EISA_signature(char * name, short iobase);
+static int  DevicePresent(short iobase);
+static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs, char *multicast_table);
+
+static int aprom_crc (struct device *dev);
+
+static void eisa_probe(struct device *dev, short iobase);
+static void pci_probe(struct device *dev, short iobase);
+static struct device *alloc_device(struct device *dev, int iobase);
+
+#ifdef MODULE
+int  init_module(void);
+void cleanup_module(void);
+# else
+static unsigned char de4x5_irq[] = {5,9,10,11};
+#endif /* MODULE */
+
+static int num_de4x5s = 0, num_eth = 0, autoprobed = 0;
+
+/*
+** Kludge to get around the fact that the CSR addresses have different
+** offsets in the PCI and EISA boards. Also note that the ethernet address
+** PROM is accessed differently.
+*/
+static struct bus_type {
+  int bus;
+  int device;
+} bus;
+
+/*
+** Miscellaneous defines...
+*/
+#define RESET_DE4X5 {\
+    long i;\
+    i=inl(DE4X5_BMR);\
+    outl(i | BMR_SWR, DE4X5_BMR);\
+    outl(i, DE4X5_BMR);\
+    for (i=0;i<5;i++) inl(DE4X5_BMR);\
+		   }
+
+
+
+
+int de4x5_probe(struct device *dev)
+{
+  int tmp = num_de4x5s, iobase = dev->base_addr;
+  int status = -ENODEV;
+
+  if ((iobase > 0) && (iobase <0x100)) {   /* Don't probe at all. */
+    status = -ENXIO;
+
+#ifdef MODULE
+  } else if (iobase == 0){
+    printk("Autoprobing is not supported when loading a module based driver.\n");
+    status = -EIO;
+#endif
+  } else {                            /* First probe for the Ethernet */
+                                      /* Address PROM pattern */
+    eisa_probe(dev, iobase);
+    pci_probe(dev, iobase);
+
+    if ((tmp == num_de4x5s) && (iobase != 0)) {
+      printk("%s: de4x5_probe() cannot find device at 0x%04x.\n", dev->name,
+                                                                  iobase);
+    }
+
+    /*
+    ** Walk the device list to check that at least one device
+    ** initialised OK
+    */
+    for (; dev->priv == NULL && dev->next != NULL; dev = dev->next);
+
+    if (dev->priv) status = 0;
+    if (iobase == 0) autoprobed = 1;
+  }
+
+  return status;
+}
+
+static int
+de4x5_hw_init(struct device *dev, short iobase)
+{
+  struct bus_type *lp = &bus;
+  int tmpbus, i, j, status=0;
+  char *tmp, name[DE4X5_NAME_LENGTH + 1];
+  u_long nicsr;
+
+  /*
+  ** First, RESET the board.
+  */
+  RESET_DE4X5;
+
+  if (((nicsr=inl(DE4X5_STS)) & (STS_TS | STS_RS)) == 0) {/* Really stopped */
+    
+    /* 
+    ** Now find out what kind of DC21040/DC21140 board we have.
+    */
+    if (lp->bus == PCI) {
+      strcpy(name,"DE435");
+    } else {
+      EISA_signature(name, EISA_ID0);
+    }
+
+    if (*name != '\0') {                         /* found a board signature */
+      dev->base_addr = iobase;
+      
+      if (lp->bus == EISA) {
+	printk("%s: %s at %#3x (EISA slot %d)", 
+	       dev->name, name, (u_short)iobase, (((u_short)iobase>>12)&0x0f));
+      } else {                                   /* PCI port address */
+	printk("%s: %s at %#3x (PCI device %d)", dev->name, name, (u_short)iobase,lp->device);
+      }
+	
+      printk(", h/w address ");
+      status = aprom_crc(dev);
+      for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+	printk("%2.2x:", dev->dev_addr[i]);
+      }
+      printk("%2.2x,\n", dev->dev_addr[i]);
+      
+      tmpbus = lp->bus;
+
+      if (status == 0) {
+	struct de4x5_private *lp;
+
+	/* 
+	** Reserve a section of kernel memory for the adapter
+	** private area and the TX/RX descriptor rings.
+	*/
+	dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + LWPAD, 
+				                                   GFP_KERNEL);
+	/*
+	** Align to a longword boundary
+	*/
+	dev->priv = (void *)(((u_long)dev->priv + LWPAD) & ~LWPAD);
+	lp = (struct de4x5_private *)dev->priv;
+	memset(dev->priv, 0, sizeof(struct de4x5_private));
+	lp->bus = tmpbus;
+
+	/*
+	** Allocate contiguous receive buffers, long word aligned. 
+	** This could be a possible memory leak if the private area
+	** is ever hosed.
+	*/
+	for (tmp=NULL, j=0; j<BUFF_ALLOC_RETRIES && tmp==NULL; j++) {
+	  if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + LWPAD, 
+	       	  		                        GFP_KERNEL)) != NULL) {
+	    tmp = (void *)(((u_long) tmp + LWPAD) & ~LWPAD);
+	    for (i=0; i<NUM_RX_DESC; i++) {
+	      lp->rx_ring[i].status = 0;
+	      lp->rx_ring[i].des1 = RX_BUFF_SZ;
+	      lp->rx_ring[i].buf = tmp + i * RX_BUFF_SZ;
+	      lp->rx_ring[i].next = NULL;
+	    }
+	  }
+	}
+
+	if (tmp != NULL) {
+	  lp->rxRingSize = NUM_RX_DESC;
+	  lp->txRingSize = NUM_TX_DESC;
+	  
+	  /* Write the end of list marker to the descriptor lists */
+	  lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
+	  lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+
+	  /* Tell the adapter where the TX/RX rings are located. */
+	  outl((u_long)lp->rx_ring, DE4X5_RRBA);
+	  outl((u_long)lp->tx_ring, DE4X5_TRBA);
+
+	  if (dev->irq < 2) {
+#ifndef MODULE
+	    unsigned char irqnum;
+	    u_long omr;
+	    autoirq_setup(0);
+	    
+	    omr = inl(DE4X5_OMR);
+	    outl(IMR_AIM|IMR_RUM, DE4X5_IMR); /* Unmask RUM interrupt */
+	    outl(OMR_SR | omr, DE4X5_OMR);    /* Start RX w/no descriptors */
+
+	    irqnum = autoirq_report(1);
+	    if (!irqnum) {
+	      printk("      and failed to detect IRQ line.\n");
+	      status = -ENXIO;
+	    } else {
+	      for (dev->irq=0,i=0; i<sizeof(de4x5_irq) && !dev->irq; i++) {
+		if (irqnum == de4x5_irq[i]) {
+		  dev->irq = irqnum;
+		  printk("      and uses IRQ%d.\n", dev->irq);
+		}
+	      }
+		  
+	      if (!dev->irq) {
+		printk("      but incorrect IRQ line detected.\n");
+		status = -ENXIO;
+	      }
+	    }
+		
+	    outl(0, DE4X5_IMR);               /* Re-mask RUM interrupt */
+#endif  /* MODULE */
+	  } else {
+	    printk("      and requires IRQ%d (not probed).\n", dev->irq);
+	  }
+	} else {
+	  printk("%s: Kernel could not allocate RX buffer memory.\n", 
+		                                                    dev->name);
+	  status = -ENXIO;
+	}
+      } else {
+	printk("      which has an Ethernet PROM CRC error.\n");
+	status = -ENXIO;
+      } 
+    } else {
+      status = -ENXIO;
+    }
+  } else {
+    status = -ENXIO;
+  }
+  
+  if (!status) {
+    if (de4x5_debug > 0) {
+      printk(version);
+    }
+    
+    /* The DE4X5-specific entries in the device structure. */
+    dev->open = &de4x5_open;
+    dev->hard_start_xmit = &de4x5_queue_pkt;
+    dev->stop = &de4x5_close;
+    dev->get_stats = &de4x5_get_stats;
+#ifdef HAVE_MULTICAST
+    dev->set_multicast_list = &set_multicast_list;
+#endif
+    dev->do_ioctl = &de4x5_ioctl;
+    
+    dev->mem_start = 0;
+    
+    /* Fill in the generic field of the device structure. */
+    ether_setup(dev);
+  } else {                            /* Incorrectly initialised hardware */
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    if (lp) {
+      kfree_s(lp->rx_ring, RX_BUFF_SZ * NUM_RX_DESC + LWPAD);
+    }
+    if (dev->priv) {
+      kfree_s(dev->priv, sizeof(struct de4x5_private) + LWPAD);
+      dev->priv = NULL;
+    }
+  }
+
+  return status;
+}
+
+
+static int
+de4x5_open(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  short iobase = dev->base_addr;
+  int i, status = 0;
+  u_long imr, omr, sts;
+
+  /*
+  ** Stop the TX and RX...
+  */
+  STOP_DE4X5;
+
+  if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, "de4x5")) {
+    printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
+    status = -EAGAIN;
+  } else {
+
+    irq2dev_map[dev->irq] = dev;
+    /* 
+    ** Re-initialize the DE4X5... 
+    */
+    status = de4x5_init(dev);
+
+    if (de4x5_debug > 1){
+      printk("%s: de4x5 open with irq %d\n",dev->name,dev->irq);
+      printk("\tphysical address: ");
+      for (i=0;i<6;i++){
+	printk("%2.2x:",(short)dev->dev_addr[i]);
+      }
+      printk("\n");
+      printk("\tchecked memory: 0x%08lx\n",eisa_slots_full);
+      printk("Descriptor head addresses:\n");
+      printk("\t0x%8.8lx  0x%8.8lx\n",(long)lp->rx_ring,(long)lp->tx_ring);
+      printk("Descriptor addresses:\nRX: ");
+      for (i=0;i<lp->rxRingSize-1;i++){
+	if (i < 3) {
+	  printk("0x%8.8lx  ",(long)&lp->rx_ring[i].status);
+	}
+      }
+      printk("...0x%8.8lx\n",(long)&lp->rx_ring[i].status);
+      printk("TX: ");
+      for (i=0;i<lp->txRingSize-1;i++){
+	if (i < 3) {
+	  printk("0x%8.8lx  ", (long)&lp->tx_ring[i].status);
+	}
+      }
+      printk("...0x%8.8lx\n", (long)&lp->tx_ring[i].status);
+      printk("Descriptor buffers:\nRX: ");
+      for (i=0;i<lp->rxRingSize-1;i++){
+	if (i < 3) {
+	  printk("0x%8.8lx  ",(long)lp->rx_ring[i].buf);
+	}
+      }
+      printk("...0x%8.8lx\n",(long)lp->rx_ring[i].buf);
+      printk("TX: ");
+      for (i=0;i<lp->txRingSize-1;i++){
+	if (i < 3) {
+	  printk("0x%8.8lx  ", (long)lp->tx_ring[i].buf);
+	}
+      }
+      printk("...0x%8.8lx\n", (long)lp->tx_ring[i].buf);
+      printk("Ring size: \nRX: %d\nTX: %d\n", 
+	     (short)lp->rxRingSize, 
+	     (short)lp->txRingSize); 
+      printk("\tstatus:  %d\n", status);
+    }
+
+    if (!status) {
+      dev->tbusy = 0;                         
+      dev->start = 1;
+      dev->interrupt = UNMASK_INTERRUPTS;
+      
+      /*
+      ** Reset any pending interrupts
+      */
+      sts = inl(DE4X5_STS);
+      outl(sts, DE4X5_STS);
+
+      /*
+      ** Unmask and enable DE4X5 board interrupts
+      */
+      imr = 0;
+      UNMASK_IRQs;
+      ENABLE_IRQs;
+
+      START_DE4X5;
+    }
+    if (de4x5_debug > 1) {
+      printk("\tsts:  0x%08x\n", inl(DE4X5_STS));
+      printk("\tbmr:  0x%08x\n", inl(DE4X5_BMR));
+      printk("\timr:  0x%08x\n", inl(DE4X5_IMR));
+      printk("\tomr:  0x%08x\n", inl(DE4X5_OMR));
+      printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
+      printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
+      printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
+      printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
+    }
+  }
+
+#ifdef MODULE
+      MOD_INC_USE_COUNT;
+#endif       
+
+  return status;
+}
+
+/*
+** Initialize the DE4X5 operating conditions
+*/
+static int
+de4x5_init(struct device *dev)
+{  
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  short iobase = dev->base_addr;
+  int offset, status = 0;
+  u_long i, j, bmr, omr;
+  char *pa;
+
+  /* Ensure a full reset */
+  RESET_DE4X5;
+
+  /* Set up automatic transmit polling every 1.6ms */
+  bmr = inl(DE4X5_BMR);
+  bmr |= TAP_1_6MS | CAL_16LONG;
+  outl(bmr, DE4X5_BMR);
+
+  /* Set up imperfect filtering mode as default, turn off promiscuous mode */
+  omr = OMR_HP;
+  offset = IMPERF_PA_OFFSET;
+
+  /* Lock out other processes whilst setting up the hardware */
+  set_bit(0, (void *)&dev->tbusy);
+
+  /* Rewrite the descriptor lists' start addresses */
+  outl((u_long)lp->rx_ring, DE4X5_RRBA);  /* Start of RX Descriptor List */
+  outl((u_long)lp->tx_ring, DE4X5_TRBA);  /* Start of TX Descriptor List */
+
+  /* Reset the buffer pointers */
+  lp->rx_new = lp->rx_old = 0;
+  lp->tx_new = lp->tx_old = 0;
+
+  /* Initialize each descriptor ownership in the RX ring */
+  for (i = 0; i < lp->rxRingSize; i++) {
+    lp->rx_ring[i].status = R_OWN;
+  }
+
+  /* Initialize each descriptor ownership in the TX ring */
+  for (i = 0; i < lp->txRingSize; i++) {
+    lp->tx_ring[i].status = 0;
+  }
+
+  /* Initialise the setup frame prior to starting the receive process */
+  memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
+
+  /* Insert the physical address */
+  for (pa=lp->setup_frame+offset, j=0; j<ETH_ALEN; j++) {
+    *(pa + j) = dev->dev_addr[j];
+    if (j & 0x01) pa += 2;
+  }
+
+  /* Clear the multicast list */
+  set_multicast_list(dev, 0, NULL);
+
+  /* Tell the hardware there's a new packet to be sent */
+  load_packet(dev, lp->setup_frame, HASH_F | TD_SET | SETUP_FRAME_LEN, NULL);
+
+  /* Start the TX process */
+  outl(omr|OMR_ST, DE4X5_OMR);
+
+  /* Poll for completion of setup frame (interrupts are disabled for now) */
+  for (j=0, i=0;i<100 && j==0;i++) {
+    if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+  }
+  outl(omr, DE4X5_OMR);                        /* Stop everything! */
+
+  if (i == 100) {
+    printk("%s: Setup frame timed out, status %08x\n", dev->name, 
+	                                                       inl(DE4X5_STS));
+    status = -EIO;
+  }
+
+  /* Update pointers */
+  lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+  lp->tx_old = lp->tx_new;
+
+  /* Autoconfigure the connected port */
+  if (autoconf_media(dev) == 0) {
+    status = -EIO;
+  }
+
+  return 0;
+}
+
+/* 
+** Writes a socket buffer address to the next available transmit descriptor
+*/
+static int
+de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+  volatile struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int iobase = dev->base_addr;
+  int status = 0;
+  u_long imr, omr, sts;
+
+  sts = inl(DE4X5_STS);
+
+  /* 
+  ** Transmitter timeout, possibly serious problems.
+  ** The 'lostMedia' threshold accounts for transient errors that
+  ** were noticed when switching media.
+  */
+  if (dev->tbusy || (lp->lostMedia > 3)) {
+    int tickssofar = jiffies - dev->trans_start;
+    if (tickssofar < 10 && !lp->lostMedia) {
+      /* Check if TX ring is full or not - 'tbusy' cleared if not full. */
+      if ((TX_BUFFS_AVAIL > 0) && dev->tbusy) {
+	dev->tbusy = 0;
+      }
+      status = -1;
+    } else {
+      printk("%s: transmit timed out, status %08x, tbusy:%d, lostMedia:%d tickssofar:%d, resetting.\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, tickssofar);
+	
+      /* Stop and reset the TX and RX... */
+      STOP_DE4X5;
+      status = de4x5_init(dev);
+
+      /* Unmask DE4X5 board interrupts */
+      if (!status) {
+	/* Start here to clean stale interrupts later */
+	dev->trans_start = jiffies;
+	START_DE4X5;
+
+	/* Clear any pending (stale) interrupts */
+	sts = inl(DE4X5_STS);
+	outl(sts, DE4X5_STS);
+
+	/* Unmask DE4X5 board interrupts */
+	imr = 0;
+	UNMASK_IRQs;
+	  
+	dev->interrupt = UNMASK_INTERRUPTS;
+	dev->start = 1;
+	dev->tbusy = 0;                         
+      
+	ENABLE_IRQs;
+      } else {
+	printk("%s: hardware initialisation failure, status %08x.\n",
+	                                            dev->name, inl(DE4X5_STS));
+      }
+    }
+  } else if (skb == NULL) {
+    dev_tint(dev);
+  } else if (skb->len > 0) {
+
+    /* 
+    ** Block a timer-based transmit from overlapping.  This could better be
+    ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well. 
+    */
+    if (set_bit(0, (void*)&dev->tbusy) != 0)
+      printk("%s: Transmitter access conflict.\n", dev->name);
+
+    if (TX_BUFFS_AVAIL > 0) {                   /* Fill in a Tx ring entry */
+      if (((u_long)skb->data & ~0x03) != (u_long)skb->data) {
+	printk("%s: TX skb buffer alignment prob..\n", dev->name);
+      }
+
+      load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+      outl(POLL_DEMAND, DE4X5_TPD);             /* Start the TX */
+
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize; /* Ensure a wrap */
+	
+      dev->trans_start = jiffies;
+    }
+
+    if (TX_BUFFS_AVAIL > 0) {
+      dev->tbusy = 0;                           /* Another pkt may be queued */
+    }
+  }
+
+  return status;
+}
+
+/*
+** The DE4X5 interrupt handler. 
+*/
+static void
+de4x5_interrupt(int reg_ptr)
+{
+    int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+    struct device *dev = (struct device *)(irq2dev_map[irq]);
+    struct de4x5_private *lp;
+    int iobase;
+    u_long imr, sts;
+
+    if (dev == NULL) {
+	printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq);
+    } else {
+      lp = (struct de4x5_private *)dev->priv;
+      iobase = dev->base_addr;
+
+      if (dev->interrupt)
+	printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+      dev->interrupt = MASK_INTERRUPTS;
+
+      /* 
+      ** Get the interrupt information and disable them. 
+      ** The device read will ensure pending buffers are flushed
+      ** in intermediate PCI bridges, so that the posted interrupt
+      ** has some real data to work with.
+      */
+      sts = inl(DE4X5_STS);
+      MASK_IRQs;
+
+      /* 
+      ** Acknowledge the DE4X5 board interrupts
+      */
+      outl(sts, DE4X5_STS);
+
+      if (sts & STS_RI)	                 /* Rx interrupt (packet[s] arrived) */
+	de4x5_rx(dev);
+
+      if (sts & STS_TI)                  /* Tx interrupt (packet sent) */
+	de4x5_tx(dev); 
+
+      if ((TX_BUFFS_AVAIL > 0) && dev->tbusy) { /* any resources available? */
+	dev->tbusy = 0;                  /* clear TX busy flag */
+	mark_bh(NET_BH);
+      }
+
+      dev->interrupt = UNMASK_INTERRUPTS;
+
+      UNMASK_IRQs;
+    }
+
+    return;
+}
+
+static int
+de4x5_rx(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int i, entry;
+  volatile long status;
+  char *buf;
+
+  /* Loop over any new packets for sending up the stack */
+  for (entry = lp->rx_new; lp->rx_ring[entry].status >= 0;entry = lp->rx_new) {
+    status = lp->rx_ring[entry].status;
+
+    if (status & RD_FS) {                   /* Remember the start of frame */
+      lp->rx_old = entry;
+    }
+
+    if (status & RD_LS) {                   /* Valid frame status */
+      if (status & RD_ES) {	            /* There was an error. */
+	lp->stats.rx_errors++;              /* Update the error stats. */
+	if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
+	if (status & RD_CE)           lp->stats.rx_crc_errors++;
+	if (status & RD_OF)           lp->stats.rx_fifo_errors++;
+      } else {                              /* A valid frame received */
+	struct sk_buff *skb;
+	short pkt_len = (short)(lp->rx_ring[entry].status >> 16);
+
+	if ((skb = alloc_skb(pkt_len, GFP_ATOMIC)) != NULL) {
+	  skb->len = pkt_len;
+	  skb->dev = dev;
+	
+	  if (entry < lp->rx_old) {         /* Wrapped buffer */
+	    short len = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
+	    memcpy(skb->data, lp->rx_ring[lp->rx_old].buf, len);
+	    memcpy(skb->data + len, lp->rx_ring[0].buf, pkt_len - len);
+	  } else {                          /* Linear buffer */
+	    memcpy(skb->data, lp->rx_ring[lp->rx_old].buf, pkt_len);
+	  }
+
+	  /* 
+	  ** Notify the upper protocol layers that there is another 
+	  ** packet to handle
+	  */
+	  netif_rx(skb);
+
+	  /*
+	  ** Update stats
+	  */
+	  lp->stats.rx_packets++;
+	  for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
+	    if (pkt_len < i*DE4X5_PKT_BIN_SZ) {
+	      lp->pktStats.bins[i]++;
+	      i = DE4X5_PKT_STAT_SZ;
+	    }
+	  }
+	  buf = skb->data;                  /* Look at the dest addr */
+	  if (buf[0] & 0x01) {              /* Multicast/Broadcast */
+	    if ((*(long *)&buf[0] == -1) && (*(short *)&buf[4] == -1)) {
+	      lp->pktStats.broadcast++;
+	    } else {
+	      lp->pktStats.multicast++;
+	    }
+	  } else if ((*(long *)&buf[0] == *(long *)&dev->dev_addr[0]) &&
+		     (*(short *)&buf[4] == *(short *)&dev->dev_addr[4])) {
+	    lp->pktStats.unicast++;
+	  }
+	  
+	  lp->pktStats.bins[0]++;           /* Duplicates stats.rx_packets */
+	  if (lp->pktStats.bins[0] == 0) {  /* Reset counters */
+	    memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+	  }
+	} else {
+	  printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+	  lp->stats.rx_dropped++;	      /* Really, deferred. */
+	  break;
+	}
+      }
+
+      /* Change buffer ownership for this last frame, back to the adapter */
+      for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
+	lp->rx_ring[lp->rx_old].status = R_OWN;
+      }
+      lp->rx_ring[entry].status = R_OWN;
+    }
+
+    /*
+    ** Update entry information
+    */
+    lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
+  }
+
+  return 0;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+de4x5_tx(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int entry, iobase = dev->base_addr;
+  volatile long status;
+
+  for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+    status = lp->tx_ring[entry].status;
+    if (status < 0) {                            /* Buffer not sent yet */
+      break;
+    } else if (status & TD_ES) {                 /* An error happened */
+      lp->stats.tx_errors++; 
+      if (status & TD_NC)  lp->stats.tx_carrier_errors++;
+      if (status & TD_LC)  lp->stats.tx_window_errors++;
+      if (status & TD_UF)  lp->stats.tx_fifo_errors++;
+      if (status & TD_LC)  lp->stats.collisions++;
+      if (status & TD_EC)  lp->pktStats.excessive_collisions++;
+      if (status & TD_DE)  lp->stats.tx_aborted_errors++;
+
+      if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
+	lp->lostMedia++;
+      } else {
+	outl(POLL_DEMAND, DE4X5_TPD);            /* Restart a stalled TX */
+      }
+    } else {                                     /* Packet sent */
+      lp->stats.tx_packets++;
+      lp->lostMedia = 0;                         /* Remove transient problem */
+    }
+    /* Free the buffer if it's not a setup frame. */
+    if (lp->skb[entry] != NULL) {
+      dev_kfree_skb(lp->skb[entry], FREE_WRITE);
+    }
+
+    /* Update all the pointers */
+    lp->tx_old = (++lp->tx_old) % lp->txRingSize;
+  }
+
+  return 0;
+}
+
+static int
+de4x5_close(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int iobase = dev->base_addr;
+  u_long imr, omr;
+
+  dev->start = 0;
+  dev->tbusy = 1;
+
+  if (de4x5_debug > 1) {
+    printk("%s: Shutting down ethercard, status was %8.8x.\n",
+	   dev->name, inl(DE4X5_STS));
+  }
+
+  /* 
+  ** We stop the DE4X5 here... mask interrupts and stop TX & RX
+  */
+  DISABLE_IRQs;
+
+  STOP_DE4X5;
+
+  /*
+  ** Free the associated irq
+  */
+  free_irq(dev->irq);
+  irq2dev_map[dev->irq] = 0;
+
+#ifdef MODULE
+  MOD_DEC_USE_COUNT;
+#endif    
+
+  return 0;
+}
+
+static struct enet_statistics *
+de4x5_get_stats(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int iobase = dev->base_addr;
+
+  lp->stats.rx_missed_errors = (int) inl(DE4X5_MFC);
+    
+  return &lp->stats;
+}
+
+static void load_packet(struct device *dev, char *buf, u_long flags, struct sk_buff *skb)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+  lp->tx_ring[lp->tx_new].buf = buf;
+  lp->tx_ring[lp->tx_new].des1 &= TD_TER;
+  lp->tx_ring[lp->tx_new].des1 |= flags;
+  lp->skb[lp->tx_new] = skb;
+  lp->tx_ring[lp->tx_new].status = T_OWN;
+
+  return;
+}
+/*
+** Set or clear the multicast filter for this adaptor.
+** num_addrs == -1	Promiscuous mode, receive all packets
+** num_addrs == 0	Normal mode, clear multicast list
+** num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+** 			best-effort filtering.
+** num_addrs == HASH_TABLE_LEN
+**	                Set all multicast bits
+*/
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int iobase = dev->base_addr;
+  u_long omr;
+
+  /* First, double check that the adapter is open */
+  if (irq2dev_map[dev->irq] != NULL) {
+    omr = inl(DE4X5_OMR);
+
+    if (num_addrs >= 0) {
+      SetMulticastFilter(dev, num_addrs, (char *)addrs, lp->setup_frame);
+
+      /* Tell the hardware that there's a new packet to be sent */
+      load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
+		                                        SETUP_FRAME_LEN, NULL);
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+      outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
+
+      omr &= ~OMR_PR;
+      omr |= OMR_PM;
+      outl(omr, DE4X5_OMR);
+    } else {                             /* set promiscuous mode */
+      omr |= OMR_PR;
+      omr &= ~OMR_PM;
+      outl(omr, DE4X5_OMR);
+    }
+  }
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+*/
+static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs, char *multicast_table)
+{
+  char j, bit, byte;
+  long *p = (long *) multicast_table;
+  int i;
+  u_short hashcode;
+  u_long crc, poly = CRC_POLYNOMIAL_LE;
+
+  if (num_addrs == HASH_TABLE_LEN) {
+    for (i=0; i<(HASH_TABLE_LEN >> 4); i++) {
+      *p++ = 0x0000ffff;
+    }
+  } else {
+    /* Clear the multicast table except for the broadcast bit */
+    memset(multicast_table, 0, (HASH_TABLE_LEN >> 2));
+    *(multicast_table + (HASH_TABLE_LEN >> 3) - 3) = 0x80;
+
+    /* Now update the table */
+    for (i=0;i<num_addrs;i++) {              /* for each address in the list */
+      if ((*addrs & 0x01) == 1) {            /* multicast address? */ 
+	crc = 0xffffffff;                    /* init CRC for each address */
+	for (byte=0;byte<ETH_ALEN;byte++) {  /* for each address byte */
+	                                     /* process each address bit */ 
+	  for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+	    crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+	  }
+	}
+	hashcode = crc & ((1 << 9) - 1);     /* hashcode is 9 LSb of CRC */
+
+	byte = hashcode >> 3;                /* bit[3-8] -> byte in filter */
+	bit = 1 << (hashcode & 0x07);        /* bit[0-2] -> bit in byte */
+
+	byte <<= 1;                          /* calc offset into setup frame */
+	if (byte & 0x02) {
+	  byte -= 1;
+	}
+	multicast_table[byte] |= bit;
+
+      } else {                               /* skip this address */
+	addrs += ETH_ALEN;
+      }
+    }
+  }
+
+  return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, short ioaddr)
+{
+  int i, maxSlots;
+  int status;
+  u_short iobase;
+  struct bus_type *lp = &bus;
+
+  if (!ioaddr && autoprobed) return ;    /* Been here before ! */
+
+  lp->bus = EISA;
+
+  if (ioaddr == 0) {                     /* Autoprobing */
+    iobase = EISA_SLOT_INC;              /* Get the first slot address */
+    i = 1;
+    maxSlots = MAX_EISA_SLOTS;
+  } else {                               /* Probe a specific location */
+    iobase = ioaddr;
+    i = (ioaddr >> 12);
+    maxSlots = i + 1;
+  }
+
+  for (status = -ENODEV; i<maxSlots && dev!=NULL; i++, iobase+=EISA_SLOT_INC) {
+    if (((eisa_slots_full >> i) & 0x01) == 0) {
+      if (DevicePresent(EISA_APROM) == 0) {
+	eisa_slots_full |= (0x01 << i);
+	if ((dev = alloc_device(dev, iobase)) != NULL) {
+	  if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+	    num_de4x5s++;
+	  }
+	  num_eth++;
+	}
+      }
+    } else {
+      printk("%s: EISA device already allocated at 0x%04x.\n", dev->name, iobase);
+    }
+  }
+
+  return;
+}
+
+/*
+** PCI bus I/O device probe
+*/
+#define PCI_DEVICE (dev_num << 3)
+
+static void pci_probe(struct device *dev, short ioaddr)
+
+{
+  u_char irq;
+  u_short pb, dev_num;
+  u_short i, vendor, device, status;
+  u_long class, iobase;
+  struct bus_type *lp = &bus;
+  static char pci_init = 0;
+
+  if (!ioaddr && autoprobed) return ;        /* Been here before ! */
+
+  if (!pci_init) {
+    for (i=0;i<PCI_MAX_BUS_NUM; i++) {
+      pci_slots_full[i] = 0;
+      pci_init = 1;
+    }
+  }
+
+  if (pcibios_present()) {
+    lp->bus = PCI;
+
+    for (pb = 0, dev_num = 0; dev_num < 32 && dev != NULL; dev_num++) {
+      pcibios_read_config_dword(pb, PCI_DEVICE, PCI_CLASS_REVISION, &class);
+      if (class != 0xffffffff) {
+	if (((pci_slots_full[pb] >> dev_num) & 0x01) == 0) {
+	  pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+	  pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
+	  if ((vendor == DC21040_VID) && (device == DC21040_DID)) {
+	    /* Set the device number information */
+	    lp->device = dev_num;
+
+	    /* Get the board I/O address */
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+	    iobase &= CBIO_MASK;
+
+	    /* Fetch the IRQ to be used */
+	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+
+	    /* Enable I/O Accesses and Bus Mastering */
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	    status |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
+	    pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+
+	    /* If a device is present, initialise it */
+	    if (DevicePresent(DE4X5_APROM) == 0) {
+	      pci_slots_full[pb] |= (0x01 << dev_num);
+	      if ((dev = alloc_device(dev, iobase)) != NULL) {
+		dev->irq = irq;
+		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		  num_de4x5s++;
+		}
+		num_eth++;
+	      }
+	    }
+	  }
+	} else {
+	  printk("%s: PCI device already allocated at slot %d.\n", dev->name, dev_num);
+	}
+      }
+    }
+  }
+
+  return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, int iobase)
+{
+  int addAutoProbe = 0;
+  struct device *tmp = NULL, *ret;
+  int (*init)(struct device *) = NULL;
+
+  /*
+  ** Check the device structures for an end of list or unused device
+  */
+  while (dev->next != NULL) {
+    if ((dev->base_addr == 0xffe0) || (dev->base_addr == 0)) break;
+    dev = dev->next;         /* walk through eth device list */
+    num_eth++;               /* increment eth device number */
+  }
+
+  /*
+  ** If an autoprobe is requested for another device, we must re-insert
+  ** the request later in the list. Remember the current position first.
+  */
+  if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
+    addAutoProbe++;
+    tmp = dev->next;                       /* point to the next device */
+    init = dev->init;                      /* remember the probe function */
+  }
+
+  /*
+  ** If at end of list and can't use current entry, malloc one up. 
+  ** If memory could not be allocated, print an error message.
+  */
+  if ((dev->next == NULL) &&  
+      !((dev->base_addr == 0xffe0) || (dev->base_addr == 0))){
+    dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+					 GFP_KERNEL);
+
+    dev = dev->next;                       /* point to the new device */
+    if (dev == NULL) {
+      printk("eth%d: Device not initialised, insufficient memory\n",
+	     num_eth);
+    } else {
+      /*
+      ** If the memory was allocated, point to the new memory area
+      ** and initialize it (name, I/O address, next device (NULL) and
+      ** initialisation probe routine).
+      */
+      dev->name = (char *)(dev + sizeof(struct device));
+      if (num_eth > 9999) {
+	sprintf(dev->name,"eth????");       /* New device name */
+      } else {
+	sprintf(dev->name,"eth%d", num_eth);/* New device name */
+      }
+      dev->base_addr = iobase;             /* assign the io address */
+      dev->next = NULL;                    /* mark the end of list */
+      dev->init = &de4x5_probe;            /* initialisation routine */
+      num_de4x5s++;
+    }
+  }
+  ret = dev;                               /* return current struct, or NULL */
+  
+  /*
+  ** Now figure out what to do with the autoprobe that has to be inserted.
+  ** Firstly, search the (possibly altered) list for an empty space.
+  */
+  if (ret != NULL) {
+    if (addAutoProbe) {
+      for (; (tmp->next!=NULL) && (tmp->base_addr!=0xffe0); tmp=tmp->next);
+
+      /*
+      ** If no more device structures and can't use the current one, malloc
+      ** one up. If memory could not be allocated, print an error message.
+      */
+      if ((tmp->next == NULL) && !(tmp->base_addr == 0xffe0)) {
+	tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+					     GFP_KERNEL);
+	tmp = tmp->next;                     /* point to the new device */
+	if (tmp == NULL) {
+	  printk("%s: Insufficient memory to extend the device list.\n", 
+		 dev->name);
+	} else {
+	  /*
+	  ** If the memory was allocated, point to the new memory area
+	  ** and initialize it (name, I/O address, next device (NULL) and
+	  ** initialisation probe routine).
+	  */
+	  tmp->name = (char *)(tmp + sizeof(struct device));
+	  if (num_eth > 9999) {
+	    sprintf(tmp->name,"eth????");       /* New device name */
+	  } else {
+	    sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+	  }
+	  tmp->base_addr = 0;                /* re-insert the io address */
+	  tmp->next = NULL;                  /* mark the end of list */
+	  tmp->init = init;                  /* initialisation routine */
+	}
+      } else {                               /* structure already exists */
+	tmp->base_addr = 0;                  /* re-insert the io address */
+      }
+    }
+  }
+
+  return ret;
+}
+
+/*
+** Auto configure the media here rather than setting the port at compile
+** time. This routine is called by de4x5_init() when a loss of media is
+** detected (excessive collisions, loss of carrier, no carrier or link fail
+** [TP]) to check whether the user has been sneaky and changed the port on us.
+*/
+static int autoconf_media(struct device *dev)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  int media, entry, iobase = dev->base_addr;
+  char frame[64];
+  u_long i, omr, sisr, linkBad;
+  u_long t_330ms = 920000;
+  u_long t_3s    = 8000000;
+
+  /* Set up for TP port, with LEDs */
+  media = TP;
+  RESET_SIA;
+  outl(SICR_OE57 | SICR_SEL | SICR_SRL, DE4X5_SICR);
+
+  /* Test the TP port */
+  for (linkBad=1,i=0;i<t_3s && linkBad;i++) {
+    if (((sisr = inl(DE4X5_SISR)) & SISR_LKF) == 0) linkBad = 0;
+    if (sisr & SISR_NCR) break;
+  }
+    
+  if (linkBad) {
+    /* Set up for BNC (Thinwire) port, with LEDs */
+    media = BNC;
+    RESET_SIA;
+    outl(SIGR_JCK | SIGR_HUJ, DE4X5_SIGR);
+    outl(STRR_CLD | STRR_CSQ | STRR_RSQ | STRR_DREN | STRR_ECEN, DE4X5_STRR);
+    outl(SICR_OE57| SICR_OE24 | SICR_OE13 | SICR_SEL |
+ 	                                    SICR_AUI | SICR_SRL, DE4X5_SICR);
+
+    /* Wait 330ms */
+    for (i=0; i<t_330ms; i++) {
+      sisr = inl(DE4X5_SISR);
+    }
+
+    /* Make up a dummy packet with CRC error */
+    create_packet(dev, frame, sizeof(frame));
+
+    /* Setup the packet descriptor */
+    entry = lp->tx_new;                        /* Remember the ring position */
+    load_packet(dev, frame, TD_LS | TD_FS | TD_AC | sizeof(frame), NULL);
+
+    /* Start the TX process */
+    omr = inl(DE4X5_OMR);
+    outl(omr|OMR_ST, DE4X5_OMR);
+
+    /* Update pointers */
+    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+    lp->tx_old = lp->tx_new;
+
+    /* 
+    ** Poll for completion of frame (interrupts are disabled for now)...
+    ** Allow upto 3 seconds to complete.
+    */
+    for (linkBad=1,i=0;i<t_3s && linkBad;i++) {
+      if ((inl(DE4X5_SISR) & SISR_NCR) == 1) break;
+      if (lp->tx_ring[entry].status >= 0) linkBad=0;
+    }
+    
+    outl(omr, DE4X5_OMR);                        /* Stop everything! */
+
+    if (linkBad || (lp->tx_ring[entry].status & TD_ES)) {
+      /* Set up for AUI (Thickwire) port, with LEDs */
+      media = AUI;
+      RESET_SIA;
+      outl(SIGR_JCK | SIGR_HUJ, DE4X5_SIGR);
+      outl(STRR_CLD | STRR_CSQ | STRR_RSQ | STRR_DREN | STRR_ECEN, DE4X5_STRR);
+      outl(SICR_OE57| SICR_SEL | SICR_AUI | SICR_SRL, DE4X5_SICR);
+
+      /* Setup the packet descriptor */
+      entry = lp->tx_new;                      /* Remember the ring position */
+      load_packet(dev, frame, TD_LS | TD_FS | TD_AC | sizeof(frame), NULL);
+
+      /* Start the TX process */
+      omr = inl(DE4X5_OMR);
+      outl(omr|OMR_ST, DE4X5_OMR);
+
+      /* Update pointers */
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+      lp->tx_old = lp->tx_new;
+
+      /* 
+      ** Poll for completion of frame (interrupts are disabled for now)...
+      ** Allow 3 seconds to complete.
+      */
+      for (linkBad=1,i=0;i<t_3s && linkBad;i++) {
+	if ((inl(DE4X5_SISR) & SISR_NCR) == 1) break;
+	if (lp->tx_ring[entry].status >= 0) linkBad=0;
+      }
+    
+      outl(omr, DE4X5_OMR);                        /* Stop everything! */
+
+      if (linkBad || (lp->tx_ring[entry].status & TD_ES)) {
+	/* Reset the SIA */
+	outl(SICR_RESET, DE4X5_SICR);        /* Reset SIA connectivity regs */
+	outl(STRR_RESET, DE4X5_STRR);        /* Write reset values */
+	outl(SIGR_RESET, DE4X5_SIGR);        /* Write reset values */
+
+	media = NC;
+      }
+    } 
+  }
+
+  if (de4x5_debug >= 1 ) {
+    printk("%s: Media is %s.\n",dev->name, 
+                                 (media == NC  ? "unconnected to this device" :
+                                 (media == TP  ? "TP" :
+                                 (media == BNC ? "BNC" : 
+                                                 "AUI"))));
+  }
+
+  if (media) lp->lostMedia = 0;
+
+  return media;
+}
+
+/*
+** Create an Ethernet packet with an invalid CRC
+*/
+static void create_packet(struct device *dev, char *frame, int len)
+{
+  int i, j;
+  char *buf = frame;
+
+  for (i=0; i<ETH_ALEN; i++) {             /* Use this source address */
+    *buf++ = dev->dev_addr[i];
+  }
+  for (i=0; i<ETH_ALEN; i++) {             /* Use this destination address */
+    *buf++ = dev->dev_addr[i];
+  }
+  for (j=1; j>=0; j--) {                   /* Packet length (2 bytes) */
+    *buf++ = (char) ((len >> 8*j) & 0xff);
+  }
+  *buf++ = 0;                              /* Data */
+
+  for (i=len-4; i<len; i++) {              /* CRC */
+    buf[i] = 0;
+  }
+  
+  return;
+}
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static void EISA_signature(char *name, short iobase)
+{
+  unsigned long i;
+  char *signatures[] = DE4X5_SIGNATURE;
+  char ManCode[8];
+  union {
+    u_long ID;
+    u_char Id[4];
+  } Eisa;
+
+  strcpy(name, "");
+  Eisa.ID = inl(iobase);
+
+  ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+  ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+  ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+  ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+  ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+  ManCode[5]='\0';
+
+  for (i=0;*signatures[i] != '\0' && *name == '\0';i++) {
+    if (strstr(ManCode, signatures[i]) != NULL) {
+      strcpy(name,ManCode);
+    }
+  }
+
+  return;                                   /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DIGITAL network adapter products.
+*/
+
+static int DevicePresent(short aprom_addr)
+{
+  static short fp=1, sigLength=0;
+  static char devSig[] = ETH_PROM_SIG;
+  char data;
+  long i, j;
+  int status = 0;
+  struct bus_type *lp = &bus;
+  static char asc2hex(char value);
+
+/* 
+** Convert the ascii signature to a hex equivalent & pack in place 
+*/
+  if (fp) {                               /* only do this once!... */
+    for (i=0,j=0;devSig[i] != '\0' && !status;i+=2,j++) {
+      if ((devSig[i]=asc2hex(devSig[i]))>=0) {
+	devSig[i]<<=4;
+	if((devSig[i+1]=asc2hex(devSig[i+1]))>=0){
+	  devSig[j]=devSig[i]+devSig[i+1];
+	} else {
+	  status= -1;
+	}
+      } else {
+	status= -1;
+      }
+    }
+    sigLength=j;
+    fp = 0;
+  }
+
+/* 
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+  if (!status) {
+    long tmp;
+    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+      if (lp->bus == PCI) {
+	while ((tmp = inl(aprom_addr)) < 0);
+	data = (char)tmp;
+      } else {
+	data = inb(aprom_addr);
+      }
+      if (devSig[j] == data) {    /* track signature */
+	j++;
+      } else {                    /* lost signature; begin search again */
+	j=0;
+      }
+    }
+
+    if (j!=sigLength) {
+      status = -ENODEV;           /* search failed */
+    }
+  }
+
+  return status;
+}
+
+static int aprom_crc(struct device *dev)
+{
+  int iobase = dev->base_addr;
+  long i, k, tmp;
+  unsigned short j,chksum;
+  unsigned char status = 0;
+  struct bus_type *lp = &bus;
+
+  for (i=0,k=0,j=0;j<3;j++) {
+    k <<= 1 ;
+    if (k > 0xffff) k-=0xffff;
+
+    if (lp->bus == PCI) {
+      while ((tmp = inl(DE4X5_APROM)) < 0);
+      k += (u_char) tmp;
+      dev->dev_addr[i++] = (u_char) tmp;
+      while ((tmp = inl(DE4X5_APROM)) < 0);
+      k += (u_short) (tmp << 8);
+      dev->dev_addr[i++] = (u_char) tmp;
+    } else {
+      k += (u_char) (tmp = inb(EISA_APROM));
+      dev->dev_addr[i++] = (u_char) tmp;
+      k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
+      dev->dev_addr[i++] = (u_char) tmp;
+    }
+
+    if (k > 0xffff) k-=0xffff;
+  }
+  if (k == 0xffff) k=0;
+
+  if (lp->bus == PCI) {
+    while ((tmp = inl(DE4X5_APROM)) < 0);
+    chksum = (u_char) tmp;
+    while ((tmp = inl(DE4X5_APROM)) < 0);
+    chksum |= (u_short) (tmp << 8);
+  } else {
+    chksum = (u_char) inb(EISA_APROM);
+    chksum |= (u_short) (inb(EISA_APROM) << 8);
+  }
+
+  if (k != chksum) status = -1;
+
+  return status;
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
+  int i, j, iobase = dev->base_addr, status = 0;
+  u_long omr;
+  union {
+    unsigned char  addr[HASH_TABLE_LEN * ETH_ALEN];
+    unsigned short sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+    unsigned long  lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+  } tmp;
+
+  switch(ioc->cmd) {
+  case DE4X5_GET_HWADDR:             /* Get the hardware address */
+    for (i=0; i<ETH_ALEN; i++) {
+      tmp.addr[i] = dev->dev_addr[i];
+    }
+    ioc->len = ETH_ALEN;
+    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+    break;
+  case DE4X5_SET_HWADDR:             /* Set the hardware address */
+    if (suser()) {
+      int offset;
+      char *pa;
+      u_long omr;
+
+      memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+      for (i=0; i<ETH_ALEN; i++) {
+	dev->dev_addr[i] = tmp.addr[i];
+      }
+      omr = inl(DE4X5_OMR);
+      if (omr & OMR_HP) {
+	offset = IMPERF_PA_OFFSET;
+      } else {
+	offset = PERF_PA_OFFSET;
+      }
+      /* Insert the physical address */
+      for (pa=lp->setup_frame+offset, i=0; i<ETH_ALEN; i++) {
+	*(pa + i) = dev->dev_addr[i];
+	if (i & 0x01) pa += 2;
+      }
+      /* Set up the descriptor and give ownership to the card */
+      while (set_bit(0, (void *)&dev->tbusy) != 0); /* Wait for lock to free */
+      load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
+		                                        SETUP_FRAME_LEN, NULL);
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+      outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
+      dev->tbusy = 0;                              /* Unlock the TX ring */
+
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_SET_PROM:               /* Set Promiscuous Mode */
+    if (suser()) {
+      omr = inl(DE4X5_OMR);
+      omr |= OMR_PR;
+      omr &= ~OMR_PM;
+      outl(omr, DE4X5_OMR);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_CLR_PROM:               /* Clear Promiscuous Mode */
+    if (suser()) {
+      omr = inl(DE4X5_OMR);
+      omr &= ~OMR_PR;
+      outb(omr, DE4X5_OMR);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_SAY_BOO:                /* Say "Boo!" to the kernel log file */
+    printk("%s: Boo!\n", dev->name);
+
+    break;
+  case DE4X5_GET_MCA:                /* Get the multicast address table */
+    ioc->len = (HASH_TABLE_LEN >> 3);
+    memcpy_tofs(ioc->data, lp->setup_frame, 192); 
+
+    break;
+  case DE4X5_SET_MCA:                /* Set a multicast address */
+    if (suser()) {
+      if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
+	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+      }
+      set_multicast_list(dev, ioc->len, tmp.addr);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_CLR_MCA:                /* Clear all multicast addresses */
+    if (suser()) {
+      set_multicast_list(dev, 0, NULL);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_MCA_EN:                 /* Enable multicast addressing */
+    if (suser()) {
+      omr = inl(DE4X5_OMR);
+      omr |= OMR_PM;
+      omr &= ~OMR_PR;
+      outl(omr, DE4X5_OMR);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_GET_STATS:              /* Get the driver statistics */
+    cli();
+    memcpy_tofs(ioc->data, &lp->pktStats, sizeof(lp->pktStats)); 
+    ioc->len = DE4X5_PKT_STAT_SZ;
+    sti();
+
+    break;
+  case DE4X5_CLR_STATS:              /* Zero out the driver statistics */
+    if (suser()) {
+      cli();
+      memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+      sti();
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_GET_OMR:                /* Get the OMR Register contents */
+    tmp.addr[0] = inl(DE4X5_OMR);
+    memcpy_tofs(ioc->data, tmp.addr, 1);
+
+    break;
+  case DE4X5_SET_OMR:                /* Set the OMR Register contents */
+    if (suser()) {
+      memcpy_fromfs(tmp.addr, ioc->data, 1);
+      outl(tmp.addr[0], DE4X5_OMR);
+    } else {
+      status = -EPERM;
+    }
+
+    break;
+  case DE4X5_GET_REG:                /* Get the DE4X5 Registers */
+    tmp.lval[0] = inl(DE4X5_STS);
+    tmp.lval[1] = inl(DE4X5_BMR);
+    tmp.lval[2] = inl(DE4X5_IMR);
+    tmp.lval[3] = inl(DE4X5_OMR);
+    tmp.lval[4] = inl(DE4X5_SISR);
+    tmp.lval[5] = inl(DE4X5_SICR);
+    tmp.lval[6] = inl(DE4X5_STRR);
+    tmp.lval[7] = inl(DE4X5_SIGR);
+    memcpy_tofs(ioc->data, tmp.addr, 32);
+
+    break;
+
+#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+
+  case DE4X5_DUMP:
+    j = 0;
+    tmp.addr[j++] = dev->irq;
+    for (i=0; i<ETH_ALEN; i++) {
+      tmp.addr[j++] = dev->dev_addr[i];
+    }
+    tmp.addr[j++] = lp->rxRingSize;
+    tmp.lval[j>>2] = eisa_slots_full; j+=4;
+    tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+    tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
+
+    for (i=0;i<lp->rxRingSize-1;i++){
+      if (i < 3) {
+	tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+      }
+    }
+    tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+    for (i=0;i<lp->txRingSize-1;i++){
+      if (i < 3) {
+	tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+      }
+    }
+    tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+
+    for (i=0;i<lp->rxRingSize-1;i++){
+      if (i < 3) {
+	tmp.lval[j>>2] = (long)lp->rx_ring[i].buf; j+=4;
+      }
+    }
+    tmp.lval[j>>2] = (long)lp->rx_ring[i].buf; j+=4;
+    for (i=0;i<lp->txRingSize-1;i++){
+      if (i < 3) {
+	tmp.lval[j>>2] = (long)lp->tx_ring[i].buf; j+=4;
+      }
+    }
+    tmp.lval[j>>2] = (long)lp->tx_ring[i].buf; j+=4;
+
+    for (i=0;i<lp->rxRingSize;i++){
+      tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+    }
+    for (i=0;i<lp->txRingSize;i++){
+      tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+    }
+
+    tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
+    tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; 
+
+    tmp.addr[j++] = lp->txRingSize;
+    tmp.addr[j++] = dev->tbusy;
+
+    ioc->len = j;
+    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+    break;
+  default:
+    status = -EOPNOTSUPP;
+  }
+
+  return status;
+}
+
+static char asc2hex(char value)
+{
+  value -= 0x30;                  /* normalise to 0..9 range */
+  if (value >= 0) {
+    if (value > 9) {              /* but may not be 10..15 */
+      value &= 0x1f;              /* make A..F & a..f be the same */
+      value -= 0x07;              /* normalise to 10..15 range */
+      if ((value < 0x0a) || (value > 0x0f)) { /* if outside range then... */
+	value = -1;               /* ...signal error */
+      }
+    }
+  } else {                        /* outside 0..9 range... */
+    value = -1;                   /* ...signal error */
+  }
+  return value;                   /* return hex char or error */
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device thisDE4X5 = {
+  "        ", /* device name inserted by /linux/drivers/net/net_init.c */
+  0, 0, 0, 0,
+  0x2000, 10, /* I/O address, IRQ */
+  0, 0, 0, NULL, de4x5_probe };
+	
+int io=0x2000;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+int irq=10;	/* or use the insmod io= irq= options 		*/
+
+int
+init_module(void)
+{
+  thisDE4X5.base_addr=io;
+  thisDE4X5.irq=irq;
+  if (register_netdev(&thisDE4X5) != 0)
+    return -EIO;
+  return 0;
+}
+
+void
+cleanup_module(void)
+{
+  if (MOD_IN_USE) {
+    printk("%s: device busy, remove delayed\n",thisDE4X5.name);
+  } else {
+    unregister_netdev(&thisDE4X5);
+  }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ *  kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *
+ *  module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ * End:
+ */
+
+
+
diff -u --recursive --new-file v1.1.85/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h
--- v1.1.85/linux/drivers/net/de4x5.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/de4x5.h	Thu Jan 26 06:53:58 1995
@@ -0,0 +1,488 @@
+/*
+    Copyright 1994 Digital Equipment Corporation.
+
+    This software may be used and distributed according to  the terms of the
+    GNU Public License, incorporated herein by reference.
+
+    The author may    be  reached as davies@wanton.lkg.dec.com  or   Digital
+    Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+    =========================================================================
+*/
+
+/*
+** DC21040 CSR<1..15> Register Address Map
+*/
+#define DE4X5_BMR    iobase+(0x000 << lp->bus)  /* Bus Mode Register */
+#define DE4X5_TPD    iobase+(0x008 << lp->bus)  /* Transmit Poll Demand Reg */
+#define DE4X5_RPD    iobase+(0x010 << lp->bus)  /* Receive Poll Demand Reg */
+#define DE4X5_RRBA   iobase+(0x018 << lp->bus)  /* RX Ring Base Address Reg */
+#define DE4X5_TRBA   iobase+(0x020 << lp->bus)  /* TX Ring Base Address Reg */
+#define DE4X5_STS    iobase+(0x028 << lp->bus)  /* Status Register */
+#define DE4X5_OMR    iobase+(0x030 << lp->bus)  /* Operation Mode Register */
+#define DE4X5_IMR    iobase+(0x038 << lp->bus)  /* Interrupt Mask Register */
+#define DE4X5_MFC    iobase+(0x040 << lp->bus)  /* Missed Frame Counter */
+#define DE4X5_APROM  iobase+(0x048 << lp->bus)  /* Ethernet Address PROM */
+#define DE4X5_DDR    iobase+(0x050 << lp->bus)  /* Data Diagnostic Register */
+#define DE4X5_FDR    iobase+(0x058 << lp->bus)  /* Full Duplex Register */
+#define DE4X5_SISR   iobase+(0x060 << lp->bus)  /* SIA Status Register */
+#define DE4X5_SICR   iobase+(0x068 << lp->bus)  /* SIA Connectivity Register */
+#define DE4X5_STRR   iobase+(0x070 << lp->bus)  /* SIA TX/RX Register */
+#define DE4X5_SIGR   iobase+(0x078 << lp->bus)  /* SIA General Register */
+
+/*
+** EISA Register Address Map
+*/
+#define EISA_ID      iobase+0x0c80   /* EISA ID Registers */ 
+#define EISA_ID0     iobase+0x0c80   /* EISA ID Register 0 */ 
+#define EISA_ID1     iobase+0x0c81   /* EISA ID Register 1 */ 
+#define EISA_ID2     iobase+0x0c82   /* EISA ID Register 2 */ 
+#define EISA_ID3     iobase+0x0c83   /* EISA ID Register 3 */ 
+#define EISA_CR      iobase+0x0c84   /* EISA Control Register */
+#define EISA_REG0    iobase+0x0c88   /* EISA Configuration Register 0 */
+#define EISA_REG1    iobase+0x0c89   /* EISA Configuration Register 1 */
+#define EISA_REG2    iobase+0x0c8a   /* EISA Configuration Register 2 */
+#define EISA_REG3    iobase+0x0c8f   /* EISA Configuration Register 3 */
+#define EISA_APROM   iobase+0x0c90   /* Ethernet Address PROM */
+
+/*
+** PCI/EISA Configuration Registers Address Map
+*/
+#define PCI_CFID     iobase+0x0008   /* PCI Configuration ID Register */
+#define PCI_CFCS     iobase+0x000c   /* PCI Command/Status Register */
+#define PCI_CFRV     iobase+0x0018   /* PCI Revision Register */
+#define PCI_CFLT     iobase+0x001c   /* PCI Latency Timer Register */
+#define PCI_CBIO     iobase+0x0028   /* PCI Base I/O Register */
+#define PCI_CBMA     iobase+0x002c   /* PCI Base Memory Address Register */
+#define PCI_CFIT     iobase+0x0038   /* PCI Configuration Interrupt Register */
+
+/*
+** EISA Configuration Register 0 bit definitions
+*/
+#define ER0_BSW       0x80           /* EISA Bus Slave Width, 1: 32 bits */
+#define ER0_BMW       0x40           /* EISA Bus Master Width, 1: 32 bits */
+#define ER0_EPT       0x20           /* EISA PREEMPT Time, 0: 23 BCLKs */
+#define ER0_ISTS      0x10           /* Interrupt Status (X) */
+#define ER0_LI        0x08           /* Latch Interrupts */
+#define ER0_INTL      0x06           /* INTerrupt Level */
+#define ER0_INTT      0x01           /* INTerrupt Type, 0: Level, 1: Edge */
+
+/*
+** EISA Configuration Register 1 bit definitions
+*/
+#define ER1_IAM       0xe0           /* ISA Address Mode */
+#define ER1_IAE       0x10           /* ISA Addressing Enable */
+#define ER1_UPIN      0x0f           /* User Pins */
+
+/*
+** EISA Configuration Register 2 bit definitions
+*/
+#define ER2_BRS       0xc0           /* Boot ROM Size */
+#define ER2_BRA       0x3c           /* Boot ROM Adress <16:13> */
+
+/*
+** EISA Configuration Register 3 bit definitions
+*/
+#define ER3_BWE       0x40           /* Burst Write Enable */
+#define ER3_BRE       0x04           /* Burst Read Enable */
+#define ER3_LSR       0x02           /* Local Software Reset */
+
+/*
+** PCI Configuration ID Register (PCI_CFID)
+*/
+#define CFID_DID    0xff00           /* Device ID */
+#define CFID_VID    0x00ff           /* Vendor ID */
+#define DC21040_DID 0x0002           /* Unique Device ID # */
+#define DC21040_VID 0x1011           /* DC21040 Manufacturer */
+
+/*
+** PCI Configuration Command/Status Register (PCI_CFCS)
+*/
+#define CFCS_DPE    0x80000000       /* Detected Parity Error (S) */
+#define CFCS_SSE    0x40000000       /* Signal System Error   (S) */
+#define CFCS_RMA    0x20000000       /* Receive Master Abort  (S) */
+#define CFCS_RTA    0x10000000       /* Receive Target Abort  (S) */
+#define CFCS_DST    0x06000000       /* DEVSEL Timing         (S) */
+#define CFCS_DPR    0x01000000       /* Data Parity Report    (S) */
+#define CFCS_FBB    0x00800000       /* Fast Back-To-Back     (S) */
+#define CFCS_SLE    0x00000100       /* System Error Enable   (C) */
+#define CFCS_PER    0x00000040       /* Parity Error Response (C) */
+#define CFCS_MO     0x00000004       /* Master Operation      (C) */
+#define CFCS_MSA    0x00000002       /* Memory Space Access   (C) */
+#define CFCS_IOSA   0x00000001       /* I/O Space Access      (C) */
+
+/*
+** PCI Configuration Revision Register (PCI_CFRV)
+*/
+#define CFRV_BC     0xff000000       /* Base Class */
+#define CFRV_SC     0x00ff0000       /* Subclass */
+#define CFRV_SN     0x000000f0       /* Step Number */
+#define CFRV_RN     0x0000000f       /* Revision Number */
+#define BASE_CLASS  0x02000000       /* Indicates Network Controller */
+#define SUB_CLASS   0x00000000       /* Indicates Ethernet Controller */
+#define STEP_NUMBER 0x00000020       /* Increments for future chips */
+#define REV_NUMBER  0x00000003       /* 0x00, 0x01, 0x02, 0x03: Rev in Step */
+#define CFRV_MASK   0xffff0000       /* Register mask */
+
+/*
+** PCI Configuration Latency Timer Register (PCI_CFLT)
+*/
+#define CFLT_BC     0x0000ff00       /* Latency Timer bits */
+
+/*
+** PCI Configuration Base I/O Address Register (PCI_CBIO)
+*/
+#define CBIO_MASK   0x0000ff80       /* Base I/O Address Mask */
+#define CBIO_IOSI   0x00000001       /* I/O Space Indicator (RO, value is 1) */
+
+/*
+** DC21040 Bus Mode Register (DE4X5_BMR)
+*/
+#define BMR_TAP    0x00060000       /* Transmit Automatic Polling */
+#define BMR_DAS    0x00010000       /* Diagnostic Address Space */
+#define BMR_CAL    0x0000c000       /* Cache Alignment */
+#define BMR_PBL    0x00003f00       /* Programmable Burst Length */
+#define BMR_BLE    0x00000080       /* Big/Little Endian */
+#define BMR_DSL    0x0000007c       /* Descriptor Skip Length */
+#define BMR_BAR    0x00000002       /* Bus ARbitration */
+#define BMR_SWR    0x00000001       /* Software Reset */
+
+#define TAP_NOPOLL 0x00000000       /* No automatic polling */
+#define TAP_200US  0x00020000       /* TX automatic polling every 200us */
+#define TAP_800US  0x00040000       /* TX automatic polling every 800us */
+#define TAP_1_6MS  0x00060000       /* TX automatic polling every 1.6ms */
+
+#define CAL_NOUSE  0x00000000       /* Not used */
+#define CAL_8LONG  0x00004000       /* 8-longword alignment */
+#define CAL_16LONG 0x00008000       /* 16-longword alignment */
+#define CAL_32LONG 0x0000c000       /* 32-longword alignment */
+
+#define PBL_0      0x00000000       /*  DMA burst length = amount in RX FIFO */
+#define PBL_1      0x00000100       /*  1 longword  DMA burst length */
+#define PBL_2      0x00000200       /*  2 longwords DMA burst length */
+#define PBL_4      0x00000400       /*  4 longwords DMA burst length */
+#define PBL_8      0x00000800       /*  8 longwords DMA burst length */
+#define PBL_16     0x00001000       /* 16 longwords DMA burst length */
+#define PBL_32     0x00002000       /* 32 longwords DMA burst length */
+
+#define DSL_1      0x00000004       /*  1 longword  / descriptor */
+#define DSL_2      0x00000008       /*  2 longwords / descriptor */
+#define DSL_4      0x00000010       /*  4 longwords / descriptor */
+#define DSL_8      0x00000020       /*  8 longwords / descriptor */
+#define DSL_16     0x00000040       /* 16 longwords / descriptor */
+#define DSL_32     0x00000080       /* 32 longwords / descriptor */
+
+/*
+** DC21040 Transmit Poll Demand Register (DE4X5_TPD)
+*/
+#define TPD        0x00000001       /* Transmit Poll Demand */
+
+/*
+** DC21040 Receive Poll Demand Register (DE4X5_RPD)
+*/
+#define RPD        0x00000001       /* Receive Poll Demand */
+
+/*
+** DC21040 Receive Ring Base Address Register (DE4X5_RRBA)
+*/
+#define RRBA       0xfffffffc       /* RX Descriptor List Start Address */
+
+/*
+** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA)
+*/
+#define TRBA       0xfffffffc       /* TX Descriptor List Start Address */
+
+/*
+** DC21040 Status Register (DE4X5_STS)
+*/
+#define STS_EB     0x03800000       /* Error Bits */
+#define STS_TS     0x00700000       /* Transmit Process State */
+#define STS_RS     0x000e0000       /* Receive Process State */
+#define STS_NIS    0x00010000       /* Normal Interrupt Summary */
+#define STS_AIS    0x00008000       /* Abnormal Interrupt Summary */
+#define STS_SE     0x00002000       /* System Error */
+#define STS_LNF    0x00001000       /* Link Fail */
+#define STS_FD     0x00000800       /* Full-Duplex Short Frame Received */
+#define STS_AT     0x00000400       /* AUI/TP Pin */
+#define STS_RWT    0x00000200       /* Receive Watchdog Time-Out */
+#define STS_RPS    0x00000100       /* Receive Process Stopped */
+#define STS_RU     0x00000080       /* Receive Buffer Unavailable */
+#define STS_RI     0x00000040       /* Receive Interrupt */
+#define STS_UNF    0x00000020       /* Transmit Underflow */
+#define STS_TJT    0x00000008       /* Transmit Jabber Time-Out */
+#define STS_TU     0x00000004       /* Transmit Buffer Unavailable */
+#define STS_TPS    0x00000002       /* Transmit Process Stopped */
+#define STS_TI     0x00000001       /* Transmit Interrupt */
+
+#define EB_PAR     0x00000000       /* Parity Error */
+#define EB_MA      0x00800000       /* Master Abort */
+#define EB_TA      0x01000000       /* Target Abort */
+#define EB_RES0    0x01800000       /* Reserved */
+#define EB_RES1    0x02000000       /* Reserved */
+
+#define TS_STOP    0x00000000       /* Stopped */
+#define TS_FTD     0x00100000       /* Fetch Transmit Descriptor */
+#define TS_WEOT    0x00200000       /* Wait for End Of Transmission */
+#define TS_QDAT    0x00300000       /* Queue skb data into TX FIFO */
+#define TS_RES     0x00400000       /* Reserved */
+#define TS_SPKT    0x00500000       /* Setup Packet */
+#define TS_SUSP    0x00600000       /* Suspended */
+#define TS_CLTD    0x00700000       /* Close Transmit Descriptor */
+
+#define RS_STOP    0x00000000       /* Stopped */
+#define RS_FRD     0x00020000       /* Fetch Receive Descriptor */
+#define RS_CEOR    0x00040000       /* Check for End of Receive Packet */
+#define RS_WFRP    0x00060000       /* Wait for Receive Packet */
+#define RS_SUSP    0x00080000       /* Suspended */
+#define RS_CLRD    0x000a0000       /* Close Receive Descriptor */
+#define RS_FLUSH   0x000c0000       /* Flush RX FIFO */
+#define RS_QRFS    0x000e0000       /* Queue RX FIFO into RX Skb */
+
+#define INT_CANCEL 0x0001ffff       /* For zeroing all interrupt sources */
+
+/*
+** DC21040 Operation Mode Register (DE4X5_OMR)
+*/
+#define OMR_CA     0x00020000       /* Capture Effect Enable */
+#define OMR_BP     0x00010000       /* Back Pressure */
+#define OMR_TR     0x0000c000       /* Threshold Control Bits */
+#define OMR_ST     0x00002000       /* Start/Stop Transmission Command */
+#define OMR_FC     0x00001000       /* Force Collisio Mode */
+#define OMR_OM     0x00000c00       /* Operating Mode */
+#define OMR_FD     0x00000200       /* Full Duplex Mode */
+#define OMR_FKD    0x00000100       /* Flaky Oscillator Disable */
+#define OMR_PM     0x00000080       /* Pass All Multicast */
+#define OMR_PR     0x00000040       /* Promiscuous Mode */
+#define OMR_SB     0x00000020       /* Start/Stop Backoff Counter */
+#define OMR_IF     0x00000010       /* Inverse Filtering */
+#define OMR_PB     0x00000008       /* Pass Bad Frames */
+#define OMR_HO     0x00000004       /* Hash Only Filtering Mode */
+#define OMR_SR     0x00000002       /* Start/Stop Receive */
+#define OMR_HP     0x00000001       /* Hash/Perfect Receive Filtering Mode */
+
+#define TR_72      0x00000000       /* Threshold set to 72 bytes */
+#define TR_96      0x00004000       /* Threshold set to 96 bytes */
+#define TR_128     0x00008000       /* Threshold set to 128 bytes */
+#define TR_160     0x0000c000       /* Threshold set to 160 bytes */
+
+/*
+** DC21040 Interrupt Mask Register (DE4X5_IMR)
+*/
+#define IMR_NIM    0x00010000       /* Normal Interrupt Summary Mask */
+#define IMR_AIM    0x00008000       /* Abnormal Interrupt Summary Mask */
+#define IMR_SEM    0x00002000       /* System Error Mask */
+#define IMR_LFM    0x00001000       /* Link Fail Mask */
+#define IMR_FDM    0x00000800       /* Full-Duplex Mask */
+#define IMR_ATM    0x00000400       /* AUI/TP Switch Mask */
+#define IMR_RWM    0x00000200       /* Receive Watchdog Time-Out Mask */
+#define IMR_RSM    0x00000100       /* Receive Stopped Mask */
+#define IMR_RUM    0x00000080       /* Receive Buffer Unavailable Mask */
+#define IMR_RIM    0x00000040       /* Receive Interrupt Mask */
+#define IMR_UNM    0x00000020       /* Underflow Interrupt Mask */
+#define IMR_TJM    0x00000008       /* Transmit Time-Out Jabber Mask */
+#define IMR_TUM    0x00000004       /* Transmit Buffer Unavailable Mask */
+#define IMR_TSM    0x00000002       /* Transmission Stopped Mask */
+#define IMR_TIM    0x00000001       /* Transmit Interrupt Mask */
+
+/*
+** DC21040 Missed Frame Counter (DE4X5_MFC)
+*/
+#define MFC_OVFL   0x00010000       /* Counter Overflow Bit */
+#define MFC_CNTR   0x0000ffff       /* Counter Bits */
+
+/*
+** DC21040 Ethernet Address PROM (DE4X5_APROM)
+*/
+#define APROM_DN   0x80000000       /* Data Not Valid */
+#define APROM_DT   0x000000ff       /* Address Byte */
+
+/*
+** DC21040 Full Duplex Register (DE4X5_FDR)
+*/
+#define FDR_FDACV  0x0000ffff       /* Full Duplex Auto Configuration Value */
+
+/*
+** DC21040 SIA Status Register (DE4X5_SISR)
+*/
+#define SISR_DAO   0x00000080       /* PLL All One */
+#define SISR_DAZ   0x00000040       /* PLL All Zero */
+#define SISR_DSP   0x00000020       /* PLL Self-Test Pass */
+#define SISR_DSD   0x00000010       /* PLL Self-Test Done */
+#define SISR_APS   0x00000008       /* Auto Polarity State */
+#define SISR_LKF   0x00000004       /* Link Fail Status */
+#define SISR_NCR   0x00000002       /* Network Connection Error */
+#define SISR_PAUI  0x00000001       /* AUI_TP Indication */
+
+#define SIA_RESET  0x00000000       /* SIA Reset */
+
+/*
+** DC21040 SIA Connectivity Register (DE4X5_SICR)
+*/
+#define SICR_OE57  0x00008000       /* Output Enable 5 6 7 */
+#define SICR_OE24  0x00004000       /* Output Enable 2 4 */
+#define SICR_OE13  0x00002000       /* Output Enable 1 3 */
+#define SICR_IE    0x00001000       /* Input Enable */
+#define SICR_EXT   0x00000000       /* SIA MUX Select External SIA Mode */
+#define SICR_D_SIA 0x00000400       /* SIA MUX Select Diagnostics - SIA Sigs */
+#define SICR_DPLL  0x00000800       /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_APLL  0x00000a00       /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_D_RxM 0x00000c00       /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_M_RxM 0x00000d00       /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_LNKT  0x00000e00       /* SIA MUX Select Diagnostics - Link Test*/
+#define SICR_SEL   0x00000f00       /* SIA MUX Select AUI or TP with LEDs */
+#define SICR_ASE   0x00000080       /* APLL Start Enable*/
+#define SICR_SIM   0x00000040       /* Serial Interface Input Multiplexer */
+#define SICR_ENI   0x00000020       /* Encoder Input Multiplexer */
+#define SICR_EDP   0x00000010       /* SIA PLL External Input Enable */
+#define SICR_AUI   0x00000008       /* 10Base-T or AUI */
+#define SICR_CAC   0x00000004       /* CSR Auto Configuration */
+#define SICR_PS    0x00000002       /* Pin AUI/TP Selection */
+#define SICR_SRL   0x00000001       /* SIA Reset */
+#define SICR_RESET 0xffff0000       /* Reset value for SICR */
+
+/*
+** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+*/
+#define STRR_SPP   0x00004000       /* Set Polarit Plus */
+#define STRR_APE   0x00002000       /* Auto Polarity Enable */
+#define STRR_LTE   0x00001000       /* Link Test Enable */
+#define STRR_SQE   0x00000800       /* Signal Quality Enable */
+#define STRR_CLD   0x00000400       /* Collision Detect Enable */
+#define STRR_CSQ   0x00000200       /* Collision Squelch Enable */
+#define STRR_RSQ   0x00000100       /* Receive Squelch Enable */
+#define STRR_CPEN  0x00000030       /* Compensation Enable */
+#define STRR_LSE   0x00000008       /* Link Pulse Send Enable */
+#define STRR_DREN  0x00000004       /* Driver Enable */
+#define STRR_LBK   0x00000002       /* Loopback Enable */
+#define STRR_ECEN  0x00000001       /* Encoder Enable */
+#define STRR_RESET 0xffffffff       /* Reset value for STRR */
+
+/*
+** DC21040 SIA General Register (DE4X5_SIGR)
+*/
+#define SIGR_FRL   0x00002000       /* Force Receiver Low */
+#define SIGR_DPST  0x00001000       /* PLL Self Test Start */
+#define SIGR_FLF   0x00000400       /* Force Link Fail */
+#define SIGR_FUSQ  0x00000200       /* Force Unsquelch */
+#define SIGR_TSCK  0x00000100       /* Test Clock */
+#define SIGR_RWR   0x00000020       /* Receive Watchdog Release */
+#define SIGR_RWD   0x00000010       /* Receive Watchdog Disable */
+#define SIGR_JCK   0x00000004       /* Jabber Clock */
+#define SIGR_HUJ   0x00000002       /* Host Unjab */
+#define SIGR_JBD   0x00000001       /* Jabber Disable */
+#define SIGR_RESET 0xffff0000       /* Reset value for SIGR */
+
+/*
+** Receive Descriptor Bit Summary
+*/
+#define R_OWN      0x80000000       /* Own Bit */
+#define RD_FL      0x7fff0000       /* Frame Length */
+#define RD_ES      0x00008000       /* Error Summary */
+#define RD_LE      0x00004000       /* Length Error */
+#define RD_DT      0x00003000       /* Data Type */
+#define RD_RF      0x00000800       /* Runt Frame */
+#define RD_MF      0x00000400       /* Multicast Frame */
+#define RD_FS      0x00000200       /* First Descriptor */
+#define RD_LS      0x00000100       /* Last Descriptor */
+#define RD_TL      0x00000080       /* Frame Too Long */
+#define RD_CS      0x00000040       /* Collision Seen */
+#define RD_FT      0x00000020       /* Frame Type */
+#define RD_RJ      0x00000010       /* Receive Watchdog */
+#define RD_DB      0x00000004       /* Dribbling Bit */
+#define RD_CE      0x00000002       /* CRC Error */
+#define RD_OF      0x00000001       /* Overflow */
+
+#define RD_RER     0x02000000       /* Receive End Of Ring */
+#define RD_RCH     0x01000000       /* Second Address Chained */
+#define RD_RBS2    0x003ff800       /* Buffer 2 Size */
+#define RD_RBS1    0x000007ff       /* Buffer 1 Size */
+
+/*
+** Transmit Descriptor Bit Summary
+*/
+#define T_OWN      0x80000000       /* Own Bit */
+#define TD_ES      0x00008000       /* Error Summary */
+#define TD_TO      0x00004000       /* Transmit Jabber Time-Out */
+#define TD_LO      0x00000800       /* Loss Of Carrier */
+#define TD_NC      0x00000400       /* No Carrier */
+#define TD_LC      0x00000200       /* Late Collision */
+#define TD_EC      0x00000100       /* Excessive Collisions */
+#define TD_HF      0x00000080       /* Heartbeat Fail */
+#define TD_CC      0x00000078       /* Collision Counter */
+#define TD_LF      0x00000004       /* Link Fail */
+#define TD_UF      0x00000002       /* Underflow Error */
+#define TD_DE      0x00000001       /* Deferred */
+
+#define TD_IC      0x80000000       /* Interrupt On Completion */
+#define TD_LS      0x40000000       /* Last Segment */
+#define TD_FS      0x20000000       /* First Segment */
+#define TD_FT1     0x10000000       /* Filtering Type */
+#define TD_SET     0x08000000       /* Setup Packet */
+#define TD_AC      0x04000000       /* Add CRC Disable */
+#define TD_TER     0x02000000       /* Transmit End Of Ring */
+#define TD_TCH     0x01000000       /* Second Address Chained */
+#define TD_DPD     0x00800000       /* Disabled Padding */
+#define TD_FT0     0x00400000       /* Filtering Type */
+#define TD_RBS2    0x003ff800       /* Buffer 2 Size */
+#define TD_RBS1    0x000007ff       /* Buffer 1 Size */
+
+#define PERFECT_F  0x00000000
+#define HASH_F     TD_FT0
+#define INVERSE_F  TD_FT1
+#define HASH_O_F   TD_FT1| TD_F0
+
+/*
+** Miscellaneous
+*/
+#define PCI  0
+#define EISA 1
+
+#define NC         0               /* No Connection */
+#define TP         1               /* 10Base-T */
+#define BNC        2               /* Thinwire */
+#define AUI        3               /* Thickwire */
+  
+#define HASH_TABLE_LEN   512       /* Bits */
+
+#define SETUP_FRAME_LEN  192       /* Bytes */
+#define PERF_PA_OFFSET   180       /* Bytes */
+#define IMPERF_PA_OFFSET 156       /* Bytes */
+
+#define POLL_DEMAND       1
+
+#define MASK_INTERRUPTS   1
+#define UNMASK_INTERRUPTS 0
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define	DE4X5IOCTL	SIOCDEVPRIVATE
+
+struct de4x5_ioctl {
+	unsigned short cmd;                /* Command to run */
+	unsigned short len;                /* Length of the data buffer */
+	unsigned char  *data;              /* Pointer to the data buffer */
+};
+
+/* 
+** Recognised commands for the driver 
+*/
+#define DE4X5_GET_HWADDR	0x01 /* Get the hardware address */
+#define DE4X5_SET_HWADDR	0x02 /* Get the hardware address */
+#define DE4X5_SET_PROM  	0x03 /* Set Promiscuous Mode */
+#define DE4X5_CLR_PROM  	0x04 /* Clear Promiscuous Mode */
+#define DE4X5_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */
+#define DE4X5_GET_MCA   	0x06 /* Get a multicast address */
+#define DE4X5_SET_MCA   	0x07 /* Set a multicast address */
+#define DE4X5_CLR_MCA    	0x08 /* Clear a multicast address */
+#define DE4X5_MCA_EN    	0x09 /* Enable a multicast address group */
+#define DE4X5_GET_STATS  	0x0a /* Get the driver statistics */
+#define DE4X5_CLR_STATS 	0x0b /* Zero out the driver statistics */
+#define DE4X5_GET_OMR           0x0c /* Get the OMR Register contents */
+#define DE4X5_SET_OMR           0x0d /* Set the OMR Register contents */
+#define DE4X5_GET_REG           0x0e /* Get the DE4X5 Registers */
+
+
+
diff -u --recursive --new-file v1.1.85/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.1.85/linux/drivers/net/ppp.c	Thu Nov 24 14:18:33 1994
+++ linux/drivers/net/ppp.c	Tue Jan 24 09:51:26 1995
@@ -374,7 +374,7 @@
 
   /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
   if (mru < PPP_MRU)
-    mru = PPP_MRU;
+    mru = new_mru = PPP_MRU;
 
   mtu = (mtu * 2) + 20;
   mru = (mru * 2) + 20;
diff -u --recursive --new-file v1.1.85/linux/drivers/net/znet.c linux/drivers/net/znet.c
--- v1.1.85/linux/drivers/net/znet.c	Mon Jan 16 14:18:21 1995
+++ linux/drivers/net/znet.c	Thu Jan 26 07:35:23 1995
@@ -577,7 +577,7 @@
 #else
 			skb->lock = 0;
 			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_s(skb, sksize);
+				kfree(skb);
 				lp->stats.rx_dropped++;
 				break;
 			}
diff -u --recursive --new-file v1.1.85/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h
--- v1.1.85/linux/drivers/scsi/53c7,8xx.h	Tue Nov 29 10:07:12 1994
+++ linux/drivers/scsi/53c7,8xx.h	Wed Jan 25 09:06:17 1995
@@ -57,7 +57,7 @@
     	NULL, NULL,						\
 	NULL, NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset,\
         NULL, scsicam_bios_param, 					\
-	/* can queue */ 1, /* id */ 7, 255 /* old SG_ALL */, 		\
+	/* can queue */ 1, /* id */ 7, 127 /* old SG_ALL */, 		\
 	/* cmd per lun */ 1 , 0, 0, DISABLE_CLUSTERING}
 #else
 /* Register addresses, ordered numerically */
diff -u --recursive --new-file v1.1.85/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.1.85/linux/drivers/scsi/eata_dma.c	Sun Jan 22 23:03:59 1995
+++ linux/drivers/scsi/eata_dma.c	Wed Jan 25 09:12:28 1995
@@ -42,7 +42,7 @@
  *  Thanks also to Greg Hosler who did a lot of testing and *
  *  found quite a number of bugs during the development.    *
  ************************************************************
- *  last change: 95/01/20                                   *
+ *  last change: 95/01/24                                   *
  ************************************************************/
 
 /* Look in eata_dma.h for configuration information */
@@ -141,20 +141,13 @@
 	}
 
 	eata_stat = inb(base + HA_RSTATUS);	
-	DBG(DBG_INTR, printk("IRQ %d received, base 0x%04x, pid %lx, target: "
-			     "%x, lun: %x, ea_s: 0x%02x, hba_s: 0x%02x \n", 
+	DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, target: "
+			     "%x, lun: %x, ea_s: %#.2x, hba_s: %#.2x \n", 
 			     irq, base, cmd->pid, cmd->target, cmd->lun, 
 			     eata_stat, hba_stat));
 
 	switch (hba_stat) {
 	case 0x00:		/* NO Error */
-#if 0
-	    if (scsi_stat == INTERMEDIATE_GOOD && cmd->device->type != TYPE_TAPE)
-	        result = DID_ERROR << 16;
-
-	    /* If there was a bus reset, redo operation on each target */
-	    else 
-#endif
 	    if (scsi_stat == CONDITION_GOOD
 		&& cmd->device->type == TYPE_DISK
 		&& (HD(cmd)->t_state[cmd->target] == RESET))
@@ -198,37 +191,19 @@
 	    result = DID_ERROR << 16;
 	    break;
 	}
-	cmd->result = result; 
-
-#if DGB_INTR2
-	if (scsi_stat || result || hba_stat) 
-	  printk("hba_stat: 0x%02x,scsi_stat: 0x%02x, sense_key: 0x%x, "
-		 "result: 0x%08x\n", hba_stat, 
-		 scsi_stat,cmd->sense_buffer[2] & 0xf, result); 
+	cmd->result = result | scsi_stat; 
+        if (scsi_stat == CHECK_CONDITION && 
+	    (cmd->sense_buffer[2] & 0xf) == UNIT_ATTENTION) 
+	    cmd->result |= (DRIVER_SENSE << 24);
+
+#if DBG_INTR2
+	if (scsi_stat || result || hba_stat || eata_stat != 0x50) 
+	  printk("eata_stat: %#x hba_stat: %#.2x,scsi_stat: %#.2x, sense_key: %#x, "
+		 "result: %#.8x\n", eata_stat, hba_stat, 
+		 scsi_stat,cmd->sense_buffer[2] & 0xf, cmd->result); 
 	DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
 #endif
-	if (scsi_stat == CHECK_CONDITION) { 
-	    switch (cmd->sense_buffer[2] & 0xf) {
-	        case NO_SENSE:
-		case RECOVERED_ERROR:
-	            break;
-		case ILLEGAL_REQUEST:
-	            cmd->result |= (DID_BAD_TARGET << 16);
-		    break;
-		case NOT_READY:
-		case MEDIUM_ERROR:
-		case HARDWARE_ERROR:
-		case UNIT_ATTENTION:
-	        case DATA_PROTECT:
-	        case BLANK_CHECK:
-	        case COPY_ABORTED:
-	        case ABORTED_COMMAND:
-	        case VOLUME_OVERFLOW:
-	        case MISCOMPARE:
-	        default:
-	            cmd->result |= (DRIVER_SENSE << 24);
-	    }
-	}
+
 	cp->status = FREE;   /* now we can release the slot  */
  
 	restore_flags(flags);
@@ -304,7 +279,7 @@
 
     cp->status = USED;		/* claim free slot */
     
-    DBG(DBG_QUEUE, printk("eata_queue pid %lx, target: %x, lun: %x, y %d\n",
+    DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
 			  cmd->pid, cmd->target, cmd->lun, y));
     DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
  
@@ -355,7 +330,7 @@
       done(cmd);
       return (0);
     }
-    DBG(DBG_QUEUE,printk("Queued base 0x%04lx pid: %lx target: %x lun: %x slot %d irq %d\n",
+    DBG(DBG_QUEUE,printk("Queued base %#.4lx pid: %ld target: %x lun: %x slot %d irq %d\n",
 	   (long)sh->base, cmd->pid, cmd->target, cmd->lun, y, sh->irq));
     DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
     restore_flags(flags);
@@ -391,7 +366,7 @@
     save_flags(flags);
     cli();
 
-    DBG(DBG_ABNORM, printk("eata_abort called pid: %lx target: %x lun: %x reason %x\n",
+    DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x reason %x\n",
 			   cmd->pid, cmd->target, cmd->lun, cmd->abort_reason));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  
@@ -439,12 +414,12 @@
     save_flags(flags);
     cli();
 
-    DBG(DBG_ABNORM, printk("eata_reset called pid:%lx target: %x lun: %x reason %x\n",
+    DBG(DBG_ABNORM, printk("eata_reset called pid:%ld target: %x lun: %x reason %x\n",
 			   cmd->pid, cmd->target, cmd->lun, cmd->abort_reason));
 
 
     if (HD(cmd)->state == RESET) {
-	printk("eata_dma: reset, exit, already in reset.\n");
+	printk("eata_reset: exit, already in reset.\n");
 	restore_flags(flags);
         DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  	return (SCSI_RESET_ERROR);
@@ -452,7 +427,7 @@
 
     while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
         if (--loop == 0) {
- 	    printk("eata_dma: reset, exit, timeout error.\n");
+ 	    printk("eata_reset: exit, timeout error.\n");
 	    restore_flags(flags);
 	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	    return (SCSI_RESET_ERROR);
@@ -467,17 +442,17 @@
 
 	if (HD(cmd)->ccb[x].status == LOCKED) {
 	    HD(cmd)->ccb[x].status = FREE;
-	    printk("eata_dma: reset, locked slot %d forced free.\n", x);
+	    printk("eata_reset: locked slot %d forced free.\n", x);
 	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  	    continue;
 	}
 	sp = HD(cmd)->ccb[x].cmd;
 	HD(cmd)->ccb[x].status = RESET;
-	printk("eata_dma: reset, slot %d in reset, pid %ld.\n", x, sp->pid);
+	printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
         DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  
 	if (sp == NULL)
-	    panic("eata_dma: reset, slot %d, sp==NULL.\n", x);
+	    panic("eata_reset: slot %d, sp==NULL.\n", x);
             DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  
 	if (sp == cmd)
@@ -488,7 +463,7 @@
     inb((uint) (cmd->host->base) + HA_RSTATUS);   /* This might cause trouble */
     eata_send_command(0, (uint) cmd->host->base, EATA_CMD_RESET);
 
-    DBG(DBG_ABNORM, printk("eata_dma: reset, board reset done, enabling interrupts.\n"));
+    DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling interrupts.\n"));
     HD(cmd)->state = RESET;
 
     restore_flags(flags);
@@ -499,8 +474,7 @@
     save_flags(flags);
     cli();
 
-    DBG(DBG_ABNORM, printk("eata_dma: reset, interrupts disabled, loops %d.\n",
-			   limit));
+    DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled, loops %d.\n", limit));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  
     for (x = 0; x < cmd->host->can_queue; x++) {
@@ -515,7 +489,7 @@
 	/* This mailbox is still waiting for its interrupt */
 	HD(cmd)->ccb[x].status = LOCKED;
 
-	printk("eata_dma, reset, slot %d locked, DID_RESET, pid %ld done.\n",
+	printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
 	    x, sp->pid);
         DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	restore_flags(flags);
@@ -527,11 +501,11 @@
     restore_flags(flags);
 
     if (success) {
-	DBG(DBG_ABNORM, printk("eata_dma: reset, exit, success.\n"));
+	DBG(DBG_ABNORM, printk("eata_reset: exit, success.\n"));
         DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  	return (SCSI_RESET_SUCCESS);
     } else {
-	DBG(DBG_ABNORM, printk("eata_dma: reset, exit, wakeup.\n"));
+	DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
         DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
  	return (SCSI_RESET_PUNT);
     }
@@ -667,7 +641,7 @@
     DBG(DBG_REGISTER, print_config(gc));
 
     if (!gc->DMA_support) {
-	printk("HBA at 0x%08lx doesn't support DMA. Sorry\n",base);
+	printk("HBA at %#.8lx doesn't support DMA. Sorry\n",base);
 	return (FALSE);
     }
 
@@ -685,7 +659,7 @@
 	if (!request_irq(gc->IRQ, eata_int_handler, SA_INTERRUPT, "EATA-DMA")){
 	    reg_IRQ[gc->IRQ]++;
 	    if (!gc->IRQ_TR)
-		reg_IRQL[gc->IRQ] = 1;	/* IRQ is edge triggered */
+		reg_IRQL[gc->IRQ] = TRUE;	/* IRQ is edge triggered */
 
 	    /* We free it again so we can do a get_conf_dma and 
 	     * allocate the interrupt again later */
@@ -695,7 +669,7 @@
 	    return (FALSE);
 	}
     } else {			/* More than one HBA on this IRQ */
-	if (reg_IRQL[gc->IRQ]) {
+	if (reg_IRQL[gc->IRQ] == TRUE) {
 	    printk("Can't support more than one HBA on this IRQ,\n"
 		   "  if the IRQ is edge triggered. Sorry.\n");
 	    return (FALSE);
@@ -706,7 +680,7 @@
     request_region(base, 9, "eata_dma");
 
     if ((buff = get_board_data((uint)base, gc->IRQ, gc->scsi_id[3])) == NULL){
-        printk("HBA at %#x didn't react on INQUIRY. Sorry.\n", (ulong) base);
+        printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (ulong) base);
 	return (FALSE);
     }
 
@@ -764,7 +738,10 @@
  	}
 	sh->base = (char *) base;
 	sh->irq = gc->IRQ;
-	sh->dma_channel = dma_channel;
+	if (gc->DMA_valid) 
+	  sh->dma_channel = dma_channel;
+	else 
+	  sh->dma_channel = 0;
 
 	sh->this_id = gc->scsi_id[3 - i];
 
@@ -824,9 +801,9 @@
 	hd->next = NULL;	/* build a linked list of all HBAs */
 	hd->prev = last_HBA;
 	if(hd->prev != NULL)
-	    hd->prev->next = sh;
+	    SD(hd->prev)->next = sh;
 	last_HBA = sh;
-
+	
 	registered_HBAs++;
     }
     return (1);
@@ -1033,20 +1010,20 @@
         HBA_ptr = SD(HBA_ptr)->prev;
 
     printk("Registered HBAs:\n");
-    printk("HBA no. VID: Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr: QS: SG: CPL:\n");
+    printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: DMA: Ch: ID: Pr: QS: SG: CPL:\n");
     for (i = 1; i <= registered_HBAs; i++) {
-        printk("scsi%-2d: %.4s %.10s v%s 2.0%c  ", HBA_ptr->host_no, 
-	       SD(HBA_ptr)->vendor, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+        printk("scsi%-2d: %.10s v%s 2.0%c  ", HBA_ptr->host_no, 
+	       SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
 	       SD(HBA_ptr)->EATA_revision);
 	if(SD(HBA_ptr)->bustype == 'P') printk("PCI "); 
 	else if(SD(HBA_ptr)->bustype == 'E') printk("EISA"); 
 	else printk("ISA ");
-	printk(" %#.4x   %2d   %d   %d   %d  %2d  %2d   %2d\n", 
-	       (uint) HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, 
+	printk(" %#.4x   %2d   %2d   %d   %d   %d  %2d  %2d   %2d\n", 
+	       (uint) HBA_ptr->base, HBA_ptr->irq, HBA_ptr->dma_channel, SD(HBA_ptr)->channel, 
 	       HBA_ptr->this_id, SD(HBA_ptr)->primary, HBA_ptr->can_queue, 
 	       HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
         HBA_ptr = SD(HBA_ptr)->next;
-      }
+    }
     DBG(DPT_DEBUG,DELAY(1200));
 
     return (registered_HBAs);
diff -u --recursive --new-file v1.1.85/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h
--- v1.1.85/linux/drivers/scsi/eata_dma.h	Sun Jan 22 23:03:59 1995
+++ linux/drivers/scsi/eata_dma.h	Wed Jan 25 09:12:28 1995
@@ -16,7 +16,7 @@
 
 #define VER_MAJOR 2
 #define VER_MINOR 1
-#define VER_SUB   "0h"
+#define VER_SUB   "0i"
 
 /************************************************************************
  * Here you can configure your drives that are using a non-standard     *
diff -u --recursive --new-file v1.1.85/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.1.85/linux/drivers/scsi/scsi.c	Sun Jan 22 21:39:41 1995
+++ linux/drivers/scsi/scsi.c	Wed Jan 25 09:12:28 1995
@@ -410,10 +410,19 @@
 			scsi_result[1] |= 0x80;  /* removable */
 		}
 
+/*
+ *     Unfortunately the Toshiba CD-ROM XM-3401TA doesn't
+ *     understand the vendor specific mode select/sense
+ *     commands which are used by the photo cd routines in 
+ *     sr.c.
+ */
+
 	      SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
 	      if (!strncmp(scsi_result+8,"NEC",3))
 		SDpnt->manufacturer = SCSI_MAN_NEC;
-	      if (!strncmp(scsi_result+8,"TOSHIBA",7))
+	      if (!strncmp(scsi_result+8,"TOSHIBA",7) &&
+		  strncmp(scsi_result+16,"CD-ROM XM-3401TA",16) &&
+		  strncmp(scsi_result+32,"3593",4))
 		SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
 
 	      SDpnt->removable = (0x80 & 
diff -u --recursive --new-file v1.1.85/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.1.85/linux/drivers/scsi/sr.c	Sun Jan 22 21:39:41 1995
+++ linux/drivers/scsi/sr.c	Thu Jan 26 07:25:26 1995
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/cdrom.h>
@@ -406,6 +407,8 @@
     break; }
 
   scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+  /* The code above may have changed the sector size or capacity. */
+  scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
   return;
 }
 
@@ -424,6 +427,10 @@
 	if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
 	  (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
 
+#if 1	/* don't use for now - it doesn't seem to work for everybody */
+	sr_photocd(inode);
+#endif
+
 	/* If this device did not have media in the drive at boot time, then
 	   we would have been unable to get the sector size.  Check to see if
 	   this is the case, and try again.
@@ -432,10 +439,6 @@
 	if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
 	  get_sectorsize(MINOR(inode->i_rdev));
 
-#if 1	/* don't use for now - it doesn't seem to work for everybody */
-	sr_photocd(inode);
-#endif
-
 	return 0;
 }
 
@@ -691,7 +694,7 @@
 	      };  /* if need DMA fixup */
 	    };  /* for loop to fill list */
 #ifdef DEBUG
-	    printk("SG: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+	    printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
 		   this_count, 
 		   SCpnt->request.current_nr_sectors,
 		   SCpnt->request.nr_sectors);
@@ -916,6 +919,7 @@
     if(scsi_CDs[i].sector_size == 2048)
       scsi_CDs[i].capacity *= 4;
     scsi_CDs[i].needs_sector_size = 0;
+    sr_sizes[i] = scsi_CDs[i].capacity;
   };
   scsi_free(buffer, 512);
 }
diff -u --recursive --new-file v1.1.85/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v1.1.85/linux/fs/binfmt_elf.c	Sun Jan 22 23:03:59 1995
+++ linux/fs/binfmt_elf.c	Wed Jan 25 09:30:45 1995
@@ -8,6 +8,15 @@
  *
  * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
  */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -27,11 +36,6 @@
 
 #include <linux/config.h>
 
-#ifndef CONFIG_BINFMT_ELF
-#include <linux/module.h>
-#include <linux/version.h>
-#endif
-
 #include <linux/unistd.h>
 typedef int (*sysfun_p)();
 extern sysfun_p sys_call_table[];
@@ -45,7 +49,7 @@
 static int load_elf_library(int fd);
 
 struct linux_binfmt elf_format = {
-#ifdef CONFIG_BINFMT_ELF
+#ifndef MODULE
 	NULL, NULL, load_elf_binary, load_elf_library, NULL
 #else
 	NULL, &mod_use_count_, load_elf_binary, load_elf_library, NULL
@@ -314,9 +318,7 @@
 	unsigned int elf_stack;
 	char passed_fileno[6];
 	
-#ifndef CONFIG_BINFMT_ELF
 	MOD_INC_USE_COUNT;
-#endif
 
 	ibcs2_interpreter = 0;
 	status = 0;
@@ -325,9 +327,7 @@
 	
 	if (elf_ex.e_ident[0] != 0x7f ||
 	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return  -ENOEXEC;
 	}
 	
@@ -337,9 +337,7 @@
 	   (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)){
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -ENOEXEC;
 	};
 	
@@ -355,9 +353,7 @@
 	set_fs(old_fs);
 	if (retval < 0) {
 	        kfree (elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return retval;
 	}
 	
@@ -370,9 +366,7 @@
 
 	if (elf_exec_fileno < 0) {
 	        kfree (elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return elf_exec_fileno;
 	}
 	
@@ -419,9 +413,7 @@
 			if(retval < 0) {
 			  kfree (elf_phdata);
 			  kfree(elf_interpreter);
-#ifndef CONFIG_BINFMT_ELF
 			  MOD_DEC_USE_COUNT;
-#endif
 			  return retval;
 			};
 		};
@@ -436,9 +428,7 @@
 		if(retval < 0) {
 			kfree(elf_interpreter);
 			kfree(elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 			MOD_DEC_USE_COUNT;
-#endif
 			return -ELIBACC;
 		};
 		/* Now figure out which format our binary is */
@@ -455,9 +445,7 @@
 		  {
 		    kfree(elf_interpreter);
 		    kfree(elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 		    MOD_DEC_USE_COUNT;
-#endif
 		    return -ELIBBAD;
 		  };
 	}
@@ -482,9 +470,7 @@
 			      kfree(elf_interpreter);
 			}
 		        kfree (elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 			MOD_DEC_USE_COUNT;
-#endif
 			return -E2BIG;
 		}
 	}
@@ -536,9 +522,7 @@
 		    printk("Unable to load interpreter\n");
 		    kfree(elf_phdata);
 		    send_sig(SIGSEGV, current, 0);
-#ifndef CONFIG_BINFMT_ELF
 		    MOD_DEC_USE_COUNT;
-#endif
 		    return 0;
 		  };
 		};
@@ -640,9 +624,7 @@
 	start_thread(regs, elf_entry, bprm->p);
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
-#ifndef CONFIG_BINFMT_ELF
 	MOD_DEC_USE_COUNT;
-#endif
 	return 0;
 }
 
@@ -662,10 +644,7 @@
 	int error;
 	int i,j, k;
 
-#ifndef CONFIG_BINFMT_ELF
 	MOD_INC_USE_COUNT;
-#endif
-
 	len = 0;
 	file = current->files->fd[fd];
 	inode = file->f_inode;
@@ -674,18 +653,14 @@
 	set_fs(KERNEL_DS);
 	if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) {
 		SYS(close)(fd);
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -EACCES;
 	}
 	set_fs(USER_DS);
 	
 	if (elf_ex.e_ident[0] != 0x7f ||
 	    strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -ENOEXEC;
 	}
 	
@@ -693,18 +668,14 @@
 	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)){
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -ENOEXEC;
 	};
 	
 	/* Now read in all of the header information */
 	
 	if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) {
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -ENOEXEC;
 	}
 	
@@ -723,9 +694,7 @@
 	
 	if(j != 1)  {
 		kfree(elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return -ENOEXEC;
 	};
 	
@@ -745,9 +714,7 @@
 	SYS(close)(fd);
 	if (error != elf_phdata->p_vaddr & 0xfffff000) {
 	        kfree(elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 		MOD_DEC_USE_COUNT;
-#endif
 		return error;
 	}
 
@@ -760,13 +727,11 @@
 		  PROT_READ|PROT_WRITE|PROT_EXEC,
 		  MAP_FIXED|MAP_PRIVATE, 0);
 	kfree(elf_phdata);
-#ifndef CONFIG_BINFMT_ELF
 	MOD_DEC_USE_COUNT;
-#endif
 	return 0;
 }
 
-#ifndef CONFIG_BINFMT_ELF
+#ifdef MODULE
 char kernel_version[] = UTS_RELEASE;
 
 int init_module(void) {
diff -u --recursive --new-file v1.1.85/linux/fs/exec.c linux/fs/exec.c
--- v1.1.85/linux/fs/exec.c	Sun Jan 22 23:03:59 1995
+++ linux/fs/exec.c	Fri Jan 27 12:13:27 1995
@@ -110,14 +110,16 @@
 		return -EINVAL;
 	f = get_empty_filp();
 	if (!f)
-		return -EMFILE;
+		return -ENFILE;
 	fd = 0;
 	fpp = current->files->fd;
 	for (;;) {
 		if (!*fpp)
 			break;
-		if (++fd > NR_OPEN)
-			return -ENFILE;
+		if (++fd >= NR_OPEN) {
+			f->f_count--;
+			return -EMFILE;
+		}
 		fpp++;
 	}
 	*fpp = f;
diff -u --recursive --new-file v1.1.85/linux/fs/msdos/msbuffer.h linux/fs/msdos/msbuffer.h
--- v1.1.85/linux/fs/msdos/msbuffer.h	Tue Dec 27 08:37:13 1994
+++ linux/fs/msdos/msbuffer.h	Wed Jan 25 09:25:39 1995
@@ -15,6 +15,18 @@
 /* These macros exist to avoid modifying all the code */
 /* They should be removed one day I guess */
 
+/* The versionning mecanism of the modules system define those macros */
+/* This remove some warnings */
+#ifdef brelse
+	#undef brelse
+#endif
+#ifdef bread
+	#undef bread
+#endif
+#ifdef getblk
+	#undef getblk
+#endif
+
 #define brelse(b)				msdos_brelse(sb,b)
 #define bread(d,b,s)			msdos_bread(sb,b)
 #define getblk(d,b,s)			msdos_getblk(sb,b)
diff -u --recursive --new-file v1.1.85/linux/fs/open.c linux/fs/open.c
--- v1.1.85/linux/fs/open.c	Thu Jan 26 07:49:08 1995
+++ linux/fs/open.c	Fri Jan 27 11:04:56 1995
@@ -411,10 +411,10 @@
 	struct file * f;
 	int flag,error,fd;
 
-	for(fd=0 ; fd<NR_OPEN ; fd++)
+	for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++)
 		if (!current->files->fd[fd])
 			break;
-	if (fd>=NR_OPEN)
+	if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur)
 		return -EMFILE;
 	FD_CLR(fd,&current->files->close_on_exec);
 	f = get_empty_filp();
diff -u --recursive --new-file v1.1.85/linux/fs/pipe.c linux/fs/pipe.c
--- v1.1.85/linux/fs/pipe.c	Thu Jan 26 07:49:08 1995
+++ linux/fs/pipe.c	Fri Jan 27 11:04:56 1995
@@ -394,7 +394,7 @@
 	if (j<2)
 		return -ENFILE;
 	j=0;
-	for(i=0;j<2 && i<NR_OPEN;i++)
+	for(i=0;j<2 && i<NR_OPEN && i<current->rlim[RLIMIT_NOFILE].rlim_cur;i++)
 		if (!current->files->fd[i]) {
 			current->files->fd[ fd[j]=i ] = f[j];
 			j++;
diff -u --recursive --new-file v1.1.85/linux/fs/proc/net.c linux/fs/proc/net.c
--- v1.1.85/linux/fs/proc/net.c	Thu Jan 26 07:49:08 1995
+++ linux/fs/proc/net.c	Tue Jan 24 15:27:55 1995
@@ -19,6 +19,8 @@
  *	      Dusted off the code and added IPX. Fixed the 4K limit.
  * Erik Schoenfelder (schoenfr@ibr.cs.tu-bs.de)
  *	      /proc/net/snmp.
+ * Alan Cox (gw4pts@gw4pts.ampr.org) 1/95
+ *	      Added Appletalk slots
  *
  *  proc net directory handling functions
  */
@@ -56,11 +58,13 @@
 extern int ip_acct_procinfo(char *, char **, off_t, int);
 extern int ip_fw_blk_procinfo(char *, char **, off_t, int);
 extern int ip_fw_fwd_procinfo(char *, char **, off_t, int);
+extern int ip_msqhst_procinfo(char *, char **, off_t, int);
 extern int ip_mc_procinfo(char *, char **, off_t, int);
 #endif /* CONFIG_INET */
 #ifdef CONFIG_IPX
 extern int ipx_get_info(char *, char **, off_t, int);
 extern int ipx_rt_get_info(char *, char **, off_t, int);
+extern int ipx_get_interface_info(char *, char **, off_t , int);
 #endif /* CONFIG_IPX */
 #ifdef CONFIG_AX25
 extern int ax25_get_info(char *, char **, off_t, int);
@@ -71,6 +75,11 @@
 extern int nr_neigh_get_info(char *, char **, off_t, int);
 #endif /* CONFIG_NETROM */
 #endif /* CONFIG_AX25 */
+#ifdef CONFIG_ATALK
+extern int atalk_get_info(char *, char **, off_t, int);
+extern int atalk_rt_get_info(char *, char **, off_t, int);
+extern int atalk_if_get_info(char *, char **, off_t, int);
+#endif
 
 
 static struct file_operations proc_net_operations = {
@@ -130,6 +139,9 @@
 	{ PROC_NET_IPFWFWD,	10, "ip_forward"},
 	{ PROC_NET_IPBLFWD,	8,  "ip_block"},
 #endif
+#ifdef CONFIG_IP_MASQUERADE
+	{ PROC_NET_IPMSQHST,	13, "ip_masquerade"},
+#endif
 #ifdef CONFIG_IP_ACCT
 	{ PROC_NET_IPACCT,	7,  "ip_acct"},
 #endif
@@ -137,6 +149,7 @@
 #ifdef CONFIG_IPX
 	{ PROC_NET_IPX_ROUTE,	9, "ipx_route" },
 	{ PROC_NET_IPX,		3, "ipx" },
+	{ PROC_NET_IPX_INTERFACE, 13, "ipx_interface" },
 #endif /* CONFIG_IPX */
 #ifdef CONFIG_AX25
 	{ PROC_NET_AX25_ROUTE,	10, "ax25_route" },
@@ -147,6 +160,11 @@
 	{ PROC_NET_NR,		2, "nr" },
 #endif /* CONFIG_NETROM */
 #endif /* CONFIG_AX25 */
+#ifdef CONFIG_ATALK
+	{ PROC_NET_ATALK,	9, "appletalk" },
+	{ PROC_NET_AT_ROUTE,	11,"atalk_route" },
+	{ PROC_NET_ATIF,	11,"atalk_iface" },
+#endif /* CONFIG_ATALK */
 	{ 0, 0, NULL }
 };
 
@@ -277,6 +295,11 @@
 				length = ip_acct_procinfo(page, &start, file->f_pos,thistime);
 				break;
 #endif
+#ifdef CONFIG_IP_MASQUERADE
+			case PROC_NET_IPMSQHST:
+				length = ip_msqhst_procinfo(page, &start, file->f_pos,thistime);
+				break;
+#endif
 #ifdef CONFIG_INET_RARP				
 			case PROC_NET_RARP:
 				length = rarp_get_info(page,&start,file->f_pos,thistime);
@@ -284,6 +307,9 @@
 #endif /* CONFIG_INET_RARP */				
 #endif /* CONFIG_INET */
 #ifdef CONFIG_IPX
+			case PROC_NET_IPX_INTERFACE:
+				length = ipx_get_interface_info(page, &start, file->f_pos, thistime);
+				break;
 			case PROC_NET_IPX_ROUTE:
 				length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
 				break;
@@ -291,6 +317,17 @@
 				length = ipx_get_info(page,&start,file->f_pos,thistime);
 				break;
 #endif /* CONFIG_IPX */
+#ifdef CONFIG_ATALK
+			case PROC_NET_ATALK:
+				length = atalk_get_info(page, &start, file->f_pos, thistime);
+				break;
+			case PROC_NET_AT_ROUTE:
+				length = atalk_rt_get_info(page, &start, file->f_pos, thistime);
+				break;
+			case PROC_NET_ATIF:
+				length = atalk_if_get_info(page, &start, file->f_pos, thistime);
+				break;
+#endif /* CONFIG_ATALK */
 #ifdef CONFIG_AX25
 			case PROC_NET_AX25_ROUTE:
 				length = ax25_rt_get_info(page,&start,file->f_pos,thistime);
diff -u --recursive --new-file v1.1.85/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v1.1.85/linux/fs/umsdos/file.c	Thu Jan 26 07:49:09 1995
+++ linux/fs/umsdos/file.c	Wed Jan 25 09:25:39 1995
@@ -37,8 +37,10 @@
 {
 	/* We have to set the access time because msdos don't care */
 	int ret = msdos_file_read(inode,filp,buf,count);
-	inode->i_atime = CURRENT_TIME;
-	inode->i_dirt = 1;
+	if (!IS_RDONLY(inode)){
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
 	return ret;
 }
 /*
diff -u --recursive --new-file v1.1.85/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v1.1.85/linux/fs/umsdos/ioctl.c	Thu Jan 26 07:49:09 1995
+++ linux/fs/umsdos/ioctl.c	Wed Jan 25 09:25:39 1995
@@ -164,6 +164,21 @@
 				umsdos_parse (data.umsdos_dirent.name
 					,data.umsdos_dirent.name_len,&info);
 				ret = umsdos_newentry (dir,&info);
+			}else if (cmd == UMSDOS_RENAME_DOS){
+				/* #Specification: ioctl / UMSDOS_RENAME_DOS
+					A file or directory is rename in a DOS directory
+					(not moved accross directory). The source name
+					is in the dos_dirent.name field and the destination
+					is in umsdos_dirent.name field.
+
+					This ioctl allows umssync to rename a mangle file
+					name before syncing it back in the EMD.
+				*/
+				dir->i_count += 2;
+				ret = msdos_rename (dir
+					,data.dos_dirent.d_name,data.dos_dirent.d_reclen
+					,dir
+					,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
 			}else if (cmd == UMSDOS_UNLINK_EMD){
 				/* #Specification: ioctl / UMSDOS_UNLINK_EMD
 					The umsdos_dirent field of the struct umsdos_ioctl is used
diff -u --recursive --new-file v1.1.85/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
--- v1.1.85/linux/fs/umsdos/mangle.c	Thu Jan 26 07:49:09 1995
+++ linux/fs/umsdos/mangle.c	Wed Jan 25 09:25:39 1995
@@ -302,7 +302,7 @@
 			*/
 		}else{
 			/* Conforming MSDOS file name */
-			strcpy (info->fake.fname,fname);	/* GLU	C'est sur on a un 0 a la fin */
+			strncpy (info->fake.fname,fname,len);
 			info->msdos_reject = 0;
 			base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
 		}
diff -u --recursive --new-file v1.1.85/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v1.1.85/linux/fs/umsdos/namei.c	Thu Jan 26 07:49:09 1995
+++ linux/fs/umsdos/namei.c	Wed Jan 25 09:25:39 1995
@@ -319,7 +319,7 @@
 	int flags)		/* 0 == copy flags from old_name */
 					/* != 0, this is the value of flags */
 {
-	int ret = EPERM;
+	int ret = -EPERM;
 	struct umsdos_info old_info;
 	int old_ret = umsdos_parse (old_name,old_len,&old_info);
 	struct umsdos_info new_info;
@@ -334,58 +334,79 @@
 chkstk();
 		PRINTK (("ret %d ",ret));
 		if (ret == 0){
-			PRINTK (("new newentry "));
-			umsdos_ren_init(&new_info,&old_info,flags);
-			ret = umsdos_newentry (new_dir,&new_info);
-chkstk();
-			PRINTK (("ret %d %d ",ret,new_info.fake.len));
-			if (ret == 0){
-				PRINTK (("msdos_rename "));
-				old_dir->i_count++;
-				new_dir->i_count++;	/* Both inode are needed later */
-				ret = msdos_rename (old_dir
-					,old_info.fake.fname,old_info.fake.len
-					,new_dir
-					,new_info.fake.fname,new_info.fake.len);
-chkstk();
-				PRINTK (("after m_rename ret %d ",ret));
-				if (ret != 0){
-					umsdos_delentry (new_dir,&new_info
-						,S_ISDIR(new_info.entry.mode));
-chkstk();
-				}else{
-					ret = umsdos_delentry (old_dir,&old_info
-						,S_ISDIR(old_info.entry.mode));
+			/* check sticky bit on old_dir */
+			if ( !(old_dir->i_mode & S_ISVTX) || fsuser() ||
+			    current->fsuid == old_info.entry.uid ||
+			    current->fsuid == old_dir->i_uid ) {
+				/* Does new_name already exist? */
+				PRINTK(("new findentry "));
+				ret = umsdos_findentry(new_dir,&new_info,0);
+				if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */
+				    !(new_dir->i_mode & S_ISVTX) || fsuser() ||
+				    current->fsuid == new_info.entry.uid ||
+				    current->fsuid == new_dir->i_uid ) {
+					PRINTK (("new newentry "));
+					umsdos_ren_init(&new_info,&old_info,flags);
+					ret = umsdos_newentry (new_dir,&new_info);
 chkstk();
+					PRINTK (("ret %d %d ",ret,new_info.fake.len));
 					if (ret == 0){
-						/*
-							This UMSDOS_lookup does not look very useful.
-							It makes sure that the inode of the file will
-							be correctly setup (umsdos_patch_inode()) in
-							case it is already in use.
-
-							Not very efficient ...
-						*/
-						struct inode *inode;
-						new_dir->i_count++;
-						PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
-						ret = UMSDOS_lookup (new_dir,new_name,new_len
-							,&inode);
+						PRINTK (("msdos_rename "));
+						old_dir->i_count++;
+						new_dir->i_count++;	/* Both inode are needed later */
+						ret = msdos_rename (old_dir
+								    ,old_info.fake.fname,old_info.fake.len
+								    ,new_dir
+								    ,new_info.fake.fname,new_info.fake.len);
 chkstk();
+						PRINTK (("after m_rename ret %d ",ret));
 						if (ret != 0){
-							printk ("UMSDOS: partial rename for file %s\n"
-								,new_info.entry.name);
+							umsdos_delentry (new_dir,&new_info
+									 ,S_ISDIR(new_info.entry.mode));
+chkstk();
 						}else{
-							/*
-								Update f_pos so notify_change will succeed
-								if the file was already in use.
-							*/
-							umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+							ret = umsdos_delentry (old_dir,&old_info
+									       ,S_ISDIR(old_info.entry.mode));
 chkstk();
-							iput (inode);
+							if (ret == 0){
+								/*
+								   This UMSDOS_lookup does not look very useful.
+								   It makes sure that the inode of the file will
+								   be correctly setup (umsdos_patch_inode()) in
+								   case it is already in use.
+								   
+								   Not very efficient ...
+								   */
+								struct inode *inode;
+								new_dir->i_count++;
+								PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+								ret = UMSDOS_lookup (new_dir,new_name,new_len
+										     ,&inode);
+chkstk();
+								if (ret != 0){
+									printk ("UMSDOS: partial rename for file %s\n"
+										,new_info.entry.name);
+								}else{
+									/*
+									   Update f_pos so notify_change will succeed
+									   if the file was already in use.
+									   */
+									umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+chkstk();
+									iput (inode);
+								}
+							}
 						}
 					}
+				}else{
+					/* sticky bit set on new_dir */
+					PRINTK(("sticky set on new "));
+					ret = -EPERM;
 				}
+			}else{
+				/* sticky bit set on old_dir */
+				PRINTK(("sticky set on old "));
+				ret = -EPERM;
 			}
 		}
 		umsdos_unlockcreate(old_dir);
@@ -858,28 +879,37 @@
 				ret = -EBUSY;
 			}else if ((empty = umsdos_isempty (sdir)) != 0){
 				PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
-				if (empty == 1){
-					/* We have to removed the EMD file */
-					ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
-						,UMSDOS_EMD_NAMELEN);
+				/* check sticky bit */
+				if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
+				    current->fsuid == sdir->i_uid ||
+				    current->fsuid == dir->i_uid ) {
+					if (empty == 1){
+						/* We have to removed the EMD file */
+						ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+								   ,UMSDOS_EMD_NAMELEN);
+						sdir = NULL;
+					}
+					/* sdir must be free before msdos_rmdir() */
+					iput (sdir);
 					sdir = NULL;
-				}
-				/* sdir must be free before msdos_rmdir() */
-				iput (sdir);
-				sdir = NULL;
-				PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
-				if (ret == 0){
-					struct umsdos_info info;
-					dir->i_count++;
-					umsdos_parse (name,len,&info);
-					/* The findentry is there only to complete */
-					/* the mangling */
-					umsdos_findentry (dir,&info,2);
-					ret = msdos_rmdir (dir,info.fake.fname
-						,info.fake.len);
+					PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
 					if (ret == 0){
-						ret = umsdos_delentry (dir,&info,1);
+						struct umsdos_info info;
+						dir->i_count++;
+						umsdos_parse (name,len,&info);
+						/* The findentry is there only to complete */
+						/* the mangling */
+						umsdos_findentry (dir,&info,2);
+						ret = msdos_rmdir (dir,info.fake.fname
+								   ,info.fake.len);
+						if (ret == 0){
+							ret = umsdos_delentry (dir,&info,1);
+						}
 					}
+				}else{
+					/* sticky bit set and we don't have permission */
+					PRINTK(("sticky set "));
+					ret = -EPERM;
 				}
 			}else{	
 				/*
@@ -903,63 +933,72 @@
 	const char * name,
 	int len)
 {
-	struct umsdos_info info;
 	int ret = umsdos_nevercreat(dir,name,len,-EPERM);
 	if (ret == 0){
+		struct umsdos_info info;
 		ret = umsdos_parse (name,len,&info);
 		if (ret == 0){
 			umsdos_lockcreate(dir);
 			ret = umsdos_findentry(dir,&info,1);
 			if (ret == 0){
 				PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
-				if (info.entry.flags & UMSDOS_HLINK){
-					/* #Specification: hard link / deleting a link
-						When we deletes a file, and this file is a link
-						we must subtract 1 to the nlink field of the
-						hidden link.
-
-						If the count goes to 0, we delete this hidden
-						link too.
-					*/
-					/*
-						First, get the inode of the hidden link
-						using the standard lookup function.
-					*/
-					struct inode *inode;
-					dir->i_count++;
-					ret = UMSDOS_lookup (dir,name,len,&inode);
-					if (ret == 0){
-						PRINTK (("unlink nlink = %d ",inode->i_nlink));
-						inode->i_nlink--;
-						if (inode->i_nlink == 0){
-							struct inode *hdir = iget(inode->i_sb
-								,inode->u.umsdos_i.i_dir_owner);
-							struct umsdos_dirent entry;
-							ret = umsdos_inode2entry (hdir,inode,&entry);
-							if (ret == 0){
-								ret = UMSDOS_unlink (hdir,entry.name
-									,entry.name_len);
+				/* check sticky bit */
+				if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
+				    current->fsuid == info.entry.uid ||
+				    current->fsuid == dir->i_uid ) {
+					if (info.entry.flags & UMSDOS_HLINK){
+						/* #Specification: hard link / deleting a link
+						   When we deletes a file, and this file is a link
+						   we must subtract 1 to the nlink field of the
+						   hidden link.
+						   
+						   If the count goes to 0, we delete this hidden
+						   link too.
+						   */
+						/*
+						   First, get the inode of the hidden link
+						   using the standard lookup function.
+						   */
+						struct inode *inode;
+						dir->i_count++;
+						ret = UMSDOS_lookup (dir,name,len,&inode);
+						if (ret == 0){
+							PRINTK (("unlink nlink = %d ",inode->i_nlink));
+							inode->i_nlink--;
+							if (inode->i_nlink == 0){
+								struct inode *hdir = iget(inode->i_sb
+											  ,inode->u.umsdos_i.i_dir_owner);
+								struct umsdos_dirent entry;
+								ret = umsdos_inode2entry (hdir,inode,&entry);
+								if (ret == 0){
+									ret = UMSDOS_unlink (hdir,entry.name
+											     ,entry.name_len);
+								}else{
+									iput (hdir);
+								}
 							}else{
-								iput (hdir);
+								struct iattr newattrs;
+								newattrs.ia_valid = 0;
+								ret = UMSDOS_notify_change (inode, &newattrs);
 							}
-						}else{
-							struct iattr newattrs;
-							newattrs.ia_valid = 0;
-							ret = UMSDOS_notify_change (inode, &newattrs);
+							iput (inode);
 						}
-						iput (inode);
 					}
-				}
-				if (ret == 0){
-					ret = umsdos_delentry (dir,&info,0);
 					if (ret == 0){
-						PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
-						dir->i_count++;
-						ret = msdos_unlink_umsdos (dir,info.fake.fname
-							,info.fake.len);
-						PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
-							,info.entry.mode,ret));
+						ret = umsdos_delentry (dir,&info,0);
+						if (ret == 0){
+							PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
+							dir->i_count++;
+							ret = msdos_unlink_umsdos (dir,info.fake.fname
+										   ,info.fake.len);
+							PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
+								 ,info.entry.mode,ret));
+						}
 					}
+				}else{
+					/* sticky bit set and we've not got permission */
+					PRINTK(("sticky set "));
+					ret = -EPERM;
 				}
 			}
 			umsdos_unlockcreate(dir);
@@ -1002,7 +1041,7 @@
 			,new_len,0);
 		if (ret == -EEXIST){
 			/* #Specification: rename / new name exist
-				If the destination name already exist, it will
+			        If the destination name already exist, it will
 				silently be removed. EXT2 does it this way
 				and this is the spec of SUNOS. So does UMSDOS.
 
diff -u --recursive --new-file v1.1.85/linux/include/asm-mips/head.h linux/include/asm-mips/head.h
--- v1.1.85/linux/include/asm-mips/head.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/head.h	Wed Jan 25 08:54:22 1995
@@ -1,9 +0,0 @@
-#ifndef __ASM_MIPS_HEAD_H
-#define __ASM_MIPS_HEAD_H
-
-#include <linux/types.h>
-
-extern unsigned long swapper_pg_dir[1024];
-extern ulong IRQ_vectors[];
-
-#endif /* __ASM_MIPS_HEAD_H */
diff -u --recursive --new-file v1.1.85/linux/include/asm-mips/mm.h linux/include/asm-mips/mm.h
--- v1.1.85/linux/include/asm-mips/mm.h	Sun Jan 22 21:39:43 1995
+++ linux/include/asm-mips/mm.h	Wed Jan 25 08:54:22 1995
@@ -1,58 +0,0 @@
-#ifndef __ASM_MIPS_MM_H
-#define __ASM_MIPS_MM_H
-
-#if defined (__KERNEL__)
-
-/*
- * Note that we shift the lower 32bits of each EntryLo[01] entry
- * 6 bits to the left. That way we can convert the PFN into the
- * physical address by a single 'and' operation and gain 6 additional
- * bits for storing information which isn't present in a normal
- * MIPS page table.
- * I've also changed the naming of some bits so that they conform
- * the i386 naming as much as possible.
- * PAGE_USER isn't implemented in software yet.
- */
-#define PAGE_PRESENT               (1<<0)   /* implemented in software */
-#define PAGE_COW                   (1<<1)   /* implemented in software */
-#define PAGE_DIRTY                 (1<<2)   /* implemented in software */
-#define PAGE_USER                  (1<<3)   /* implemented in software */
-#define PAGE_UNUSED1               (1<<4)   /* implemented in software */
-#define PAGE_UNUSED2               (1<<5)   /* implemented in software */
-#define PAGE_GLOBAL                (1<<6)
-#define PAGE_ACCESSED              (1<<7)   /* The Mips valid bit      */
-#define PAGE_RW                    (1<<8)   /* The Mips dirty bit      */
-#define CACHE_CACHABLE_NO_WA       (0<<9)
-#define CACHE_CACHABLE_WA          (1<<9)
-#define CACHE_UNCACHED             (2<<9)
-#define CACHE_CACHABLE_NONCOHERENT (3<<9)
-#define CACHE_CACHABLE_CE          (4<<9)
-#define CACHE_CACHABLE_COW         (5<<9)
-#define CACHE_CACHABLE_CUW         (6<<9)
-#define CACHE_MASK                 (7<<9)
-
-#define PAGE_PRIVATE    (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         PAGE_COW | CACHE_CACHABLE_NO_WA)
-#define PAGE_SHARED     (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         CACHE_CACHABLE_NO_WA)
-#define PAGE_COPY       (PAGE_PRESENT | PAGE_ACCESSED | PAGE_COW | \
-                         CACHE_CACHABLE_NO_WA)
-#define PAGE_READONLY   (PAGE_PRESENT | PAGE_ACCESSED | CACHE_CACHABLE_NO_WA)
-#define PAGE_TABLE      (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         CACHE_CACHABLE_NO_WA)
-
-#ifndef __ASSEMBLY__
-
-#include <asm/system.h>
-
-extern unsigned long invalid_pg_table[1024];
-
-/*
- * memory.c & swap.c
- */
-extern void mem_init(unsigned long start_mem, unsigned long end_mem);
-#endif /* !defined (__ASSEMBLY__) */
-
-#endif /* defined (__KERNEL__) */
-
-#endif /* __ASM_MIPS_MM_H */
diff -u --recursive --new-file v1.1.85/linux/include/asm-mips/page.h linux/include/asm-mips/page.h
--- v1.1.85/linux/include/asm-mips/page.h	Sun Jan 22 21:39:43 1995
+++ linux/include/asm-mips/page.h	Wed Jan 25 08:54:23 1995
@@ -1,25 +1,51 @@
 #ifndef __ASM_MIPS_PAGE_H
 #define __ASM_MIPS_PAGE_H
 
+#define CONFIG_STRICT_MM_TYPECHECKS
+
 #ifndef __ASSEMBLY__
 
 #include <linux/linkage.h>
 
 #define invalidate()	tlbflush();
 extern asmlinkage void tlbflush(void);
-#endif
 
-			/* PAGE_SHIFT determines the page size */
-#define PAGE_SHIFT			12
-#define PGDIR_SHIFT			22
-#define PAGE_SIZE			(1UL << PAGE_SHIFT)
-#define PGDIR_SIZE			(1UL << PGDIR_SHIFT)
+typedef unsigned short mem_map_t;
 
-#define PAGE_OFFSET	0
-#define MAP_NR(addr) ((addr) >> PAGE_SHIFT)
-#define MAP_PAGE_RESERVED (1<<15)
+#ifdef CONFIG_STRICT_MM_TYPECHECKS
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long pte; } pte_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x)	((x).pte)
+#define pgd_val(x)	((x).pgd)
+#define pgprot_val(x)	((x).pgprot)
+
+#define __pte(x)	((pte_t) { (x) } )
+#define __pgd(x)	((pgd_t) { (x) } )
+#define __pgprot(x)	((pgprot_t) { (x) } )
 
-typedef unsigned short mem_map_t;
+#else /* !defined (CONFIG_STRICT_MM_TYPECHECKS) */
+/*
+ * .. while these make it easier on the compiler
+ */
+typedef unsigned long pte_t;
+typedef unsigned long pgd_t;
+typedef unsigned long pgprot_t;
+
+#define pte_val(x)	(x)
+#define pgd_val(x)	(x)
+#define pgprot_val(x)	(x)
+
+#define __pte(x)	(x)
+#define __pgd(x)	(x)
+#define __pgprot(x)	(x)
+
+#endif /* !defined (CONFIG_STRICT_MM_TYPECHECKS) */
+#endif /* __ASSEMBLY__ */
 
 /*
  * Note that we shift the lower 32bits of each EntryLo[01] entry
@@ -27,42 +53,73 @@
  * physical address by a single 'and' operation and gain 6 aditional
  * bits for storing information which isn't present in a normal
  * MIPS page table.
- * I've also changed the naming of some bits so that they conform
- * the i386 naming as much as possible.
- * PAGE_USER isn't implemented in software yet.
- */
-#define PAGE_PRESENT               (1<<0)   /* implemented in software */
-#define PAGE_COW                   (1<<1)   /* implemented in software */
-#define PAGE_DIRTY                 (1<<2)   /* implemented in software */
-#define PAGE_USER                  (1<<3)   /* implemented in software */
-#define PAGE_UNUSED1               (1<<4)   /* implemented in software */
-#define PAGE_UNUSED2               (1<<5)   /* implemented in software */
-#define PAGE_GLOBAL                (1<<6)
-#define PAGE_ACCESSED              (1<<7)   /* The MIPS valid bit      */
-#define PAGE_RW                    (1<<8)   /* The MIPS dirty bit      */
-#define CACHE_CACHABLE_NO_WA       (0<<9)
-#define CACHE_CACHABLE_WA          (1<<9)
-#define CACHE_UNCACHED             (2<<9)
-#define CACHE_CACHABLE_NONCOHERENT (3<<9)
-#define CACHE_CACHABLE_CE          (4<<9)
-#define CACHE_CACHABLE_COW         (5<<9)
-#define CACHE_CACHABLE_CUW         (6<<9)
-#define CACHE_MASK                 (7<<9)
-
-#define PAGE_PRIVATE    (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         PAGE_COW | CACHE_CACHABLE_NO_WA)
-#define PAGE_SHARED     (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         CACHE_CACHABLE_NO_WA)
-#define PAGE_COPY       (PAGE_PRESENT | PAGE_ACCESSED | PAGE_COW | \
-                         CACHE_CACHABLE_NO_WA)
-#define PAGE_READONLY   (PAGE_PRESENT | PAGE_ACCESSED | CACHE_CACHABLE_NO_WA)
-#define PAGE_TABLE      (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
-                         CACHE_CACHABLE_NO_WA)
+ */
+#define _PAGE_PRESENT               (1<<0)  /* implemented in software */
+#define _PAGE_COW                   (1<<1)  /* implemented in software */
+#define _PAGE_DIRTY                 (1<<2)  /* implemented in software */
+#define _PAGE_USER                  (1<<3)
+#define _PAGE_UNUSED1               (1<<4)
+#define _PAGE_UNUSED2               (1<<5)
+#define _PAGE_GLOBAL                (1<<6)
+#define _PAGE_ACCESSED              (1<<7)   /* The MIPS valid bit      */
+#define _PAGE_RW                    (1<<8)   /* The MIPS dirty bit      */
+#define _CACHE_CACHABLE_NO_WA       (0<<9)
+#define _CACHE_CACHABLE_WA          (1<<9)
+#define _CACHE_UNCACHED             (2<<9)
+#define _CACHE_CACHABLE_NONCOHERENT (3<<9)
+#define _CACHE_CACHABLE_CE          (4<<9)
+#define _CACHE_CACHABLE_COW         (5<<9)
+#define _CACHE_CACHABLE_CUW         (6<<9)
+#define _CACHE_MASK                 (7<<9)
+
+#define _PAGE_TABLE     (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY | \
+                         _CACHE_CACHABLE_NO_WA)
+
+#define _PAGE_CHG_MASK  (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _CACHE_MASK)
+
+#define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+#define PAGE_SHARED     __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _CACHE_CACHABLE_NO_WA)
+#define PAGE_COPY       __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_COW | _CACHE_CACHABLE_NO_WA)
+#define PAGE_READONLY   __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _CACHE_CACHABLE_NO_WA)
+#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
 
-#define PAGE_CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | CACHE_MASK)
+/*
+ * MIPS can't do page protection for execute, and considers that the same like
+ * read. Also, write permissions imply read permissions. This is the closest
+ * we can get by reasonable means..
+ */
+#define __P000	PAGE_NONE
+#define __P001	PAGE_READONLY
+#define __P010	PAGE_COPY
+#define __P011	PAGE_COPY
+#define __P100	PAGE_READONLY
+#define __P101	PAGE_READONLY
+#define __P110	PAGE_COPY
+#define __P111	PAGE_COPY
+
+#define __S000	PAGE_NONE
+#define __S001	PAGE_READONLY
+#define __S010	PAGE_SHARED
+#define __S011	PAGE_SHARED
+#define __S100	PAGE_READONLY
+#define __S101	PAGE_READONLY
+#define __S110	PAGE_SHARED
+#define __S111	PAGE_SHARED
+
+/* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT			12
+#define PGDIR_SHIFT			22
+#define PAGE_SIZE			(1UL << PAGE_SHIFT)
+#define PGDIR_SIZE			(1UL << PGDIR_SHIFT)
 
 #ifdef __KERNEL__
 
+#define PAGE_OFFSET	KERNELBASE
+#define MAP_NR(addr)	(((addr) - PAGE_OFFSET) >> PAGE_SHIFT)
+#define MAP_PAGE_RESERVED (1<<15)
+
+#if !defined (__ASSEMBLY__)
+
 /* page table for 0-4MB for everybody */
 extern unsigned long pg0[1024];
 
@@ -73,8 +130,9 @@
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
  */
-extern unsigned long __bad_page(void);
-extern unsigned long __bad_pagetable(void);
+extern pte_t __bad_page(void);
+extern pte_t * __bad_pagetable(void);
+
 extern unsigned long __zero_page(void);
 
 #define BAD_PAGETABLE __bad_pagetable()
@@ -102,10 +160,10 @@
 
 /* to find an entry in a page-table-directory */
 #define PAGE_DIR_OFFSET(tsk,address) \
-((((unsigned long)(address)) >> 22) + (unsigned long *) (tsk)->tss.pg_dir)
+((((unsigned long)(address)) >> PGDIR_SHIFT) + (pgd_t *) (tsk)->tss.pg_dir)
 
 /* to find an entry in a page-table */
-#define PAGE_PTR(address)		\
+#define PAGE_PTR(address) \
 ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
 
 /* the no. of pointers that fit on a page */
@@ -119,6 +177,58 @@
 		invalidate(); \
 } while (0)
 
-#endif /* __KERNEL__ */
+extern unsigned long high_memory;
+
+extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
+extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & _PAGE_PRESENT; }
+extern inline void pte_clear(pte_t *ptep)	{ pte_val(*ptep) = 0; }
+
+extern inline int pgd_none(pgd_t pgd)		{ return !pgd_val(pgd); }
+extern inline int pgd_bad(pgd_t pgd)		{ return (pgd_val(pgd) & ~PAGE_MASK) != _PAGE_TABLE || pgd_val(pgd) > high_memory; }
+extern inline int pgd_present(pgd_t pgd)	{ return pgd_val(pgd) & _PAGE_PRESENT; }
+extern inline void pgd_clear(pgd_t * pgdp)	{ pgd_val(*pgdp) = 0; }
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+extern inline int pte_read(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
+extern inline int pte_write(pte_t pte)		{ return pte_val(pte) & _PAGE_RW; }
+extern inline int pte_exec(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
+extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
+extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
+extern inline int pte_cow(pte_t pte)		{ return pte_val(pte) & _PAGE_COW; }
+
+extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_RW; return pte; }
+extern inline pte_t pte_rdprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
+extern inline pte_t pte_exprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
+extern inline pte_t pte_mkclean(pte_t pte)	{ pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkold(pte_t pte)	{ pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
+extern inline pte_t pte_uncow(pte_t pte)	{ pte_val(pte) &= ~_PAGE_COW; return pte; }
+extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) |= _PAGE_RW; return pte; }
+extern inline pte_t pte_mkread(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
+extern inline pte_t pte_mkexec(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
+extern inline pte_t pte_mkdirty(pte_t pte)	{ pte_val(pte) |= _PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkyoung(pte_t pte)	{ pte_val(pte) |= _PAGE_ACCESSED; return pte; }
+extern inline pte_t pte_mkcow(pte_t pte)	{ pte_val(pte) |= _PAGE_COW; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
+{ pte_t pte; pte_val(pte) = page | pgprot_val(pgprot); return pte; }
+
+extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
+
+extern inline unsigned long pte_page(pte_t pte)	{ return pte_val(pte) & PAGE_MASK; }
+extern inline unsigned long pgd_page(pgd_t pgd)	{ return pgd_val(pgd) & PAGE_MASK; }
+
+extern inline void pgd_set(pgd_t * pgdp, pte_t * ptep)
+{ pgd_val(*pgdp) = _PAGE_TABLE | (unsigned long) ptep; }
+
+#endif /* !defined (__ASSEMBLY__) */
+#endif /* defined (__KERNEL__) */
 
 #endif /* __ASM_MIPS_PAGE_H */
diff -u --recursive --new-file v1.1.85/linux/include/asm-mips/segment.h linux/include/asm-mips/segment.h
--- v1.1.85/linux/include/asm-mips/segment.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/segment.h	Wed Jan 25 08:54:23 1995
@@ -5,7 +5,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 1994 by Ralf Baechle
+ * Copyright (C) 1994, 1995 by Ralf Baechle
  *
  */
 #ifndef __ASM_MIPS_SEGMENT_H
@@ -22,199 +22,90 @@
 
 /*
  * returns the kernel segment base of a given address
- * Address space is a scarce resource on a R3000, so
- * emulate the Intel segment braindamage...
  */
 #define KSEGX(a)                (a & 0xe0000000)
 
-#define KERNEL_CS	KERNELBASE
-#define KERNEL_DS	KERNEL_CS
-
-#define USER_CS		0x00000000
-#define USER_DS		USER_CS
-
 #ifndef __ASSEMBLY__
 
 /*
- * This variable is defined in arch/mips/kernel/head.S.
+ * Beware: the xxx_fs_word functions work on 16bit words!
  */
-extern unsigned long segment_fs;
-
 #define get_fs_byte(addr) get_user_byte((char *)(addr))
 static inline unsigned char get_user_byte(const char *addr)
 {
-	return *(const char *)(((unsigned long)addr)+segment_fs);
+	return *addr;
 }
 
 #define get_fs_word(addr) get_user_word((short *)(addr))
 static inline unsigned short get_user_word(const short *addr)
 {
-	return *(const short *)(((unsigned long)addr)+segment_fs);
+	return *addr;
 }
 
 #define get_fs_long(addr) get_user_long((int *)(addr))
 static inline unsigned long get_user_long(const int *addr)
 {
-	return *(const int *)(((unsigned long)addr)+segment_fs);
+	return *addr;
 }
 
 #define get_fs_dlong(addr) get_user_dlong((long long *)(addr))
 static inline unsigned long get_user_dlong(const long long *addr)
 {
-	return *(const long long *)(((unsigned long)addr)+segment_fs);
+	return *addr;
 }
 
 #define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
 static inline void put_user_byte(char val,char *addr)
 {
-	*(char *)(((unsigned long)addr)+segment_fs) = val;
+	*addr = val;
 }
 
 #define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
 static inline void put_user_word(short val,short * addr)
 {
-	*(short *)(((unsigned long)addr)+segment_fs) = val;
+	*addr = val;
 }
 
 #define put_fs_long(x,addr) put_user_long((x),(int *)(addr))
 static inline void put_user_long(unsigned long val,int * addr)
 {
-	*(int *)(((unsigned long)addr)+segment_fs) = val;
+	*addr = val;
 }
 
 #define put_fs_dlong(x,addr) put_user_dlong((x),(int *)(addr))
 static inline void put_user_dlong(unsigned long val,long long * addr)
 {
-	*(long long *)(((unsigned long)addr)+segment_fs) = val;
+	*addr = val;
 }
 
-static inline void __generic_memcpy_tofs(void * to, const void * from, unsigned long n)
-{
-  __asm__(
-	".set\tnoreorder\n\t"
-	".set\tnoat\n"
-	"1:\tlbu\t$1,(%2)\n\t"
-	"addiu\t%2,%2,1\n\t"
-	"sb\t$1,(%1)\n\t"
-	"addiu\t%0,%0,-1\n\t"
-	"bne\t$0,%0,1b\n\t"
-	"addiu\t%1,%1,1\n\t"
-	".set\tat\n\t"
-	".set\treorder"
-	: /* no outputs */
-	:"r" (n),"r" (((long) to)| segment_fs),"r" ((long) from)
-	:"$1");
-}
-
-static inline void __constant_memcpy_tofs(void * to, const void * from, unsigned long n)
-{
-	/*
-	 * Use put_user_byte to avoid trouble with alignment.
-	 */
-	switch (n) {
-		case 0:
-			return;
-		case 1:
-			put_user_byte(*(const char *) from, (char *) to);
-			return;
-		case 2:
-			put_user_byte(*(const char *) from, (char *) to);
-			put_user_byte(*(1+(const char *) from), 1+(char *) to);
-			return;
-		case 3:
-			put_user_byte(*((const char *) from), (char *) to);
-			put_user_byte(*(1+(const char *) from), 1+(char *) to);
-			put_user_byte(*(2+(const char *) from), 2+(char *) to);
-			return;
-		case 4:
-			put_user_byte(*((const char *) from), (char *) to);
-			put_user_byte(*(1+(const char *) from), 1+(char *) to);
-			put_user_byte(*(2+(const char *) from), 2+(char *) to);
-			put_user_byte(*(3+(const char *) from), 3+(char *) to);
-			return;
-	}
-
-	__generic_memcpy_tofs(to, from, n);
-
-	return;
-}
-
-static inline void __generic_memcpy_fromfs(void * to, const void * from, unsigned long n)
-{
-  __asm__(
-	".set\tnoreorder\n\t"
-	".set\tnoat\n"
-	"1:\tlbu\t$1,(%2)\n\t"
-	"addiu\t%2,%2,1\n\t"
-	"sb\t$1,(%1)\n\t"
-	"addiu\t%0,%0,-1\n\t"
-	"bne\t$0,%0,1b\n\t"
-	"addiu\t%1,%1,1\n\t"
-	".set\tat\n\t"
-	".set\treorder"
-	: /* no outputs */
-	:"r" (n),"r" ((long) to),"r" (((long) from | segment_fs))
-	:"$1","memory");
-}
-
-static inline void __constant_memcpy_fromfs(void * to, const void * from, unsigned long n)
-{
-	/*
-	 * Use put_user_byte to avoid trouble with alignment.
-	 */
-	switch (n) {
-		case 0:
-			return;
-		case 1:
-			*(char *)to = get_user_byte((const char *) from);
-			return;
-		case 2:
-			*(char *) to = get_user_byte((const char *) from);
-			*(char *) to = get_user_byte(1+(const char *) from);
-			return;
-		case 3:
-			*(char *) to = get_user_byte((const char *) from);
-			*(char *) to = get_user_byte(1+(const char *) from);
-			*(char *) to = get_user_byte(2+(const char *) from);
-			return;
-		case 4:
-			*(char *) to = get_user_byte((const char *) from);
-			*(char *) to = get_user_byte(1+(const char *) from);
-			*(char *) to = get_user_byte(2+(const char *) from);
-			*(char *) to = get_user_byte(3+(const char *) from);
-			return;
-	}
-
-	
-	__generic_memcpy_fromfs(to, from, n);
-	return;
-}
-
-#define memcpy_fromfs(to, from, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy_fromfs((to),(from),(n)) : \
- __generic_memcpy_fromfs((to),(from),(n)))
-
-#define memcpy_tofs(to, from, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy_tofs((to),(from),(n)) : \
- __generic_memcpy_tofs((to),(from),(n)))
+#define memcpy_fromfs(to, from, n) memcpy((to),(from),(n))
+
+#define memcpy_tofs(to, from, n) memcpy((to),(from),(n))
+
+/*
+ * For segmented architectures, these are used to specify which segment
+ * to use for the above functions.
+ *
+ * MIPS is not segmented, so these are just dummies.
+ */
+
+#define KERNEL_DS 0
+#define USER_DS 1
 
 static inline unsigned long get_fs(void)
 {
-	return segment_fs;
+	return 0;
 }
 
 static inline unsigned long get_ds(void)
 {
-	return KERNEL_DS;
+	return 0;
 }
 
 static inline void set_fs(unsigned long val)
 {
-	segment_fs = val;
 }
 
-#endif
+#endif /* !__ASSEMBLY__ */
 
 #endif /* __ASM_MIPS_SEGMENT_H */
diff -u --recursive --new-file v1.1.85/linux/include/linux/if_ether.h linux/include/linux/if_ether.h
--- v1.1.85/linux/include/linux/if_ether.h	Wed Aug 10 19:26:43 1994
+++ linux/include/linux/if_ether.h	Tue Jan 24 15:27:55 1995
@@ -41,8 +41,7 @@
 #define ETH_P_AX25	0x0002		/* Dummy protocol id for AX.25  */
 #define ETH_P_ALL	0x0003		/* Every packet (be careful!!!) */
 #define ETH_P_802_2	0x0004		/* 802.2 frames 		*/
-#define ETH_P_SNAP	0x0005		/* 802.2 SNAP frames  		*/
-
+#define ETH_P_SNAP	0x0005		/* Internal only		*/
 /* This is an Ethernet frame header. */
 struct ethhdr {
   unsigned char		h_dest[ETH_ALEN];	/* destination eth addr	*/
diff -u --recursive --new-file v1.1.85/linux/include/linux/igmp.h linux/include/linux/igmp.h
--- v1.1.85/linux/include/linux/igmp.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/igmp.h	Tue Jan 24 15:32:53 1995
@@ -0,0 +1,67 @@
+/*
+ *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
+ *
+ *	Authors:
+ *		Alan Cox <Alan.Cox@linux.org>	
+ *
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IGMP_H
+#define _LINUX_IGMP_H
+
+/*
+ *	IGMP protocol structures
+ */
+
+struct igmphdr
+{
+	unsigned char type;
+	unsigned char unused;
+	unsigned short csum;
+	unsigned long group;
+};
+
+#define IGMP_HOST_MEMBERSHIP_QUERY	0x11	/* From RFC1112 */
+#define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
+#define IGMP_HOST_LEAVE_MESSAGE		0x17	/* An extra BSD seems to send */
+
+				/* 224.0.0.1 */
+#define IGMP_ALL_HOSTS		htonl(0xE0000001L)
+
+/*
+ * struct for keeping the multicast list in
+ */
+
+#ifdef __KERNEL__
+struct ip_mc_socklist
+{
+	unsigned long multiaddr[IP_MAX_MEMBERSHIPS];	/* This is a speed trade off */
+	struct device *multidev[IP_MAX_MEMBERSHIPS];
+};
+
+struct ip_mc_list 
+{
+	struct device *interface;
+	unsigned long multiaddr;
+	struct ip_mc_list *next;
+	struct timer_list timer;
+	int tm_running;
+	int users;
+};
+ 
+extern struct ip_mc_list *ip_mc_head;
+
+
+extern int igmp_rcv(struct sk_buff *, struct device *, struct options *, unsigned long, unsigned short,
+	unsigned long, int , struct inet_protocol *);
+extern void ip_mc_drop_device(struct device *dev); 
+extern int ip_mc_join_group(struct sock *sk, struct device *dev, unsigned long addr);
+extern int ip_mc_leave_group(struct sock *sk, struct device *dev,unsigned long addr);
+extern void ip_mc_drop_socket(struct sock *sk);
+#endif
+#endif
diff -u --recursive --new-file v1.1.85/linux/include/linux/inet.h linux/include/linux/inet.h
--- v1.1.85/linux/include/linux/inet.h	Tue Nov 29 16:04:04 1994
+++ linux/include/linux/inet.h	Fri Jan 27 12:17:02 1995
@@ -42,18 +42,7 @@
 #ifndef _LINUX_INET_H
 #define _LINUX_INET_H
 
-#if defined(__i386__)
-#define NET16(x)	((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
-#elif defined(__mc68000__)
-#define NET16(x)	(x)
-#elif defined(__alpha__)
-#define NET16(x)	((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
-#else
-#error change this to match your machine
-#endif
-
 #ifdef __KERNEL__
-
 
 extern void		inet_proto_init(struct net_proto *pro);
 extern char		*in_ntoa(unsigned long in);
diff -u --recursive --new-file v1.1.85/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v1.1.85/linux/include/linux/interrupt.h	Mon Jan  9 07:22:10 1995
+++ linux/include/linux/interrupt.h	Thu Jan 26 07:38:17 1995
@@ -2,6 +2,7 @@
 #ifndef _LINUX_INTERRUPT_H
 #define _LINUX_INTERRUPT_H
 
+#include <linux/linkage.h>
 #include <asm/bitops.h>
 
 struct bh_struct {
diff -u --recursive --new-file v1.1.85/linux/include/linux/ip.h linux/include/linux/ip.h
--- v1.1.85/linux/include/linux/ip.h	Thu Dec 15 08:28:07 1994
+++ linux/include/linux/ip.h	Wed Jan 25 08:54:23 1995
@@ -34,19 +34,25 @@
 	__u8	len;
 	__u8	ptr;
 	union {
-#if defined(__i386__)  
-		__u8	flags:4,
-			overflow:4;
+#if defined(__i386__)
+	__u8	flags:4,
+		overflow:4;
 #elif defined(__mc68000__)
-		__u8	overflow:4,
-			flags:4;
+	__u8	overflow:4,
+		flags:4;
+#elif defined(__MIPSEL__) 
+	__u8	flags:4,
+		overflow:4;
+#elif defined(__MIPSEB__)
+	__u8	overflow:4,
+		flags:4;
 #elif defined(__alpha__)
-		__u8	flags:4,
-			overflow:4;
+	__u8	flags:4,
+		overflow:4;
 #else
 #error	"Adjust this structure to match your CPU"
 #endif						
-		__u8	full_char;
+	__u8	full_char;
 	} x;
 	__u32	data[9];
 };
@@ -81,7 +87,13 @@
 #elif defined (__mc68000__)
 	__u8	version:4,
   		ihl:4;
-#elif defined (__alpha__)
+#elif defined(__MIPSEL__)
+	__u8	ihl:4,
+		version:4;
+#elif defined(__MIPSEB__)
+	__u8	version:4,
+  		ihl:4;
+#elif defined(__alpha__)
 	__u8	ihl:4,
 		version:4;
 #else
diff -u --recursive --new-file v1.1.85/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v1.1.85/linux/include/linux/ipx.h	Mon May 23 08:17:43 1994
+++ linux/include/linux/ipx.h	Tue Jan 24 15:27:55 1995
@@ -1,21 +1,64 @@
 #ifndef _IPX_H_
 #define _IPX_H_
+#include <linux/sockios.h>
+#define IPX_NODE_LEN	6
+#define IPX_MTU		576
 
 struct sockaddr_ipx
 {
 	short sipx_family;
 	unsigned long  sipx_network;
-	unsigned char sipx_node[6];
+	unsigned char sipx_node[IPX_NODE_LEN];
 	short sipx_port;
 	unsigned char	sipx_type;
+	unsigned char	sipx_zero;	/* 16 byte fill */
 };
 
+/*
+ *	So we can fit the extra info for SIOCSIFADDR into the address nicely
+ */
+ 
+#define sipx_primary	sipx_port
+#define sipx_internal	sipx_zero
+
+typedef struct ipx_route_definition
+{
+	unsigned long ipx_network;
+	unsigned long ipx_router_network;
+	unsigned char ipx_router_node[IPX_NODE_LEN];
+}	ipx_route_definition;
+
+typedef struct ipx_interface_definition
+{
+	unsigned long ipx_network;
+	unsigned char ipx_device[16];
+	unsigned short ipx_dlink_type;
+#define IPX_FRAME_NONE		0
+#define IPX_FRAME_SNAP		1
+#define IPX_FRAME_8022		2
+#define IPX_FRAME_ETHERII	3
+#define IPX_FRAME_8023		4
+	unsigned char ipx_primary;
+	unsigned char ipx_internal;
+	unsigned char ipx_node[IPX_NODE_LEN];
+}	ipx_interface_definition;
+	
+typedef struct ipx_config_data
+{
+	unsigned char	ipxcfg_auto_select_primary;
+	unsigned char	ipxcfg_auto_create_interfaces;
+}	ipx_config_data;
+
+/*
+ * OLD Route Definition for backware compatibility.
+ */
+
 struct ipx_route_def
 {
 	unsigned long ipx_network;
 	unsigned long ipx_router_network;
 #define IPX_ROUTE_NO_ROUTER	0
-	unsigned char ipx_router_node[6];
+	unsigned char ipx_router_node[IPX_NODE_LEN];
 	unsigned char ipx_device[16];
 	unsigned short ipx_flags;
 #define IPX_RT_SNAP		8
@@ -24,7 +67,8 @@
 #define IPX_RT_ROUTED		1
 };
 
-#define IPX_MTU		576
-
+#define SIOCAIPXITFCRT		(SIOCPROTOPRIVATE)
+#define SIOCAIPXPRISLT		(SIOCPROTOPRIVATE+1)
+#define SIOCIPXCFGDATA		(SIOCPROTOPRIVATE+2)
 #endif
 
diff -u --recursive --new-file v1.1.85/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.1.85/linux/include/linux/proc_fs.h	Sun Jan 22 21:39:44 1995
+++ linux/include/linux/proc_fs.h	Tue Jan 24 15:27:55 1995
@@ -76,8 +76,14 @@
 #endif
 #endif
 #ifdef CONFIG_IPX
+	PROC_NET_IPX_INTERFACE,
 	PROC_NET_IPX_ROUTE,
 	PROC_NET_IPX,
+#endif
+#ifdef CONFIG_ATALK
+	PROC_NET_ATALK,
+	PROC_NET_AT_ROUTE,
+	PROC_NET_ATIF,
 #endif
 #ifdef CONFIG_AX25
 	PROC_NET_AX25_ROUTE,
diff -u --recursive --new-file v1.1.85/linux/include/linux/resource.h linux/include/linux/resource.h
--- v1.1.85/linux/include/linux/resource.h	Wed Dec 28 16:38:30 1994
+++ linux/include/linux/resource.h	Fri Jan 27 11:04:56 1995
@@ -45,14 +45,14 @@
 #define RLIMIT_STACK	3		/* max stack size */
 #define RLIMIT_CORE	4		/* max core file size */
 #define RLIMIT_RSS	5		/* max resident set size */
+#define RLIMIT_NPROC	6		/* max number of processes */
+#define RLIMIT_NOFILE	7		/* max number of open files */
 
 #ifdef notdef
-#define RLIMIT_MEMLOCK	6		/* max locked-in-memory address space*/
-#define RLIMIT_NPROC	7		/* max number of processes */
-#define RLIMIT_NOFILE	8		/* max number of open files */
+#define RLIMIT_MEMLOCK	8		/* max locked-in-memory address space*/
 #endif
 
-#define RLIM_NLIMITS	6
+#define RLIM_NLIMITS	8
 
 #define RLIM_INFINITY	((long)(~0UL>>1))
 
diff -u --recursive --new-file v1.1.85/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.85/linux/include/linux/sched.h	Thu Jan 26 07:49:15 1995
+++ linux/include/linux/sched.h	Fri Jan 27 11:04:57 1995
@@ -227,7 +227,8 @@
 /* timeout */	0,0,0,0,0,0,0,0,0,0,0,0, \
 /* rlimits */   { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
 		  {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
-		  {       0, LONG_MAX}, {LONG_MAX, LONG_MAX}}, \
+		  {       0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
+		  {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, {NR_OPEN, NR_OPEN}}, \
 /* math */	0, \
 /* comm */	"swapper", \
 /* fs info */	0,NULL, \
diff -u --recursive --new-file v1.1.85/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.1.85/linux/include/linux/skbuff.h	Mon Jan  9 07:22:11 1995
+++ linux/include/linux/skbuff.h	Tue Jan 24 15:27:55 1995
@@ -75,6 +75,7 @@
 #define PACKET_MULTICAST	2
 #define PACKET_OTHERHOST	3		/* Unmatched promiscuous */
   unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
+  unsigned short		pkt_class;	/* For drivers that need to cache the packet type with the skbuff (new PPP) */
 #ifdef CONFIG_SLAVE_BALANCING
   unsigned short		in_dev_queue;
 #endif  
diff -u --recursive --new-file v1.1.85/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.1.85/linux/include/linux/sockios.h	Wed Nov 30 21:53:47 1994
+++ linux/include/linux/sockios.h	Tue Jan 24 15:27:55 1995
@@ -94,5 +94,9 @@
  
 #define SIOCDEVPRIVATE	0x89F0	/* to 89FF */
 
-
+/*
+ *	These 16 ioctl calls are protocol private
+ */
+ 
+#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
 #endif	/* _LINUX_SOCKIOS_H */
diff -u --recursive --new-file v1.1.85/linux/include/linux/tasks.h linux/include/linux/tasks.h
--- v1.1.85/linux/include/linux/tasks.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/tasks.h	Fri Jan 27 11:04:57 1995
@@ -6,4 +6,7 @@
  */
 #define NR_TASKS	128
 
+#define MAX_TASKS_PER_USER (NR_TASKS/2)
+#define MIN_TASKS_LEFT_FOR_ROOT 4
+
 #endif
diff -u --recursive --new-file v1.1.85/linux/include/linux/tcp.h linux/include/linux/tcp.h
--- v1.1.85/linux/include/linux/tcp.h	Fri Dec  2 11:07:04 1994
+++ linux/include/linux/tcp.h	Wed Jan 25 08:54:23 1995
@@ -17,7 +17,6 @@
 #ifndef _LINUX_TCP_H
 #define _LINUX_TCP_H
 
-#include <asm/types.h>
 
 #define HEADER_SIZE	64		/* maximum header size		*/
 
@@ -38,6 +37,26 @@
 		urg:1,
 		res2:2;
 #elif defined(__mc68000__)
+	__u16	res2:2,
+		urg:1,
+		ack:1,
+		psh:1,
+		rst:1,
+		syn:1,
+		fin:1,
+		doff:4,
+		res1:4;
+#elif defined(__MIPSEL__)
+	__u16	res1:4,
+		doff:4,
+		fin:1,
+		syn:1,
+		rst:1,
+		psh:1,
+		ack:1,
+		urg:1,
+		res2:2;
+#elif defined(__MIPSEB__)
 	__u16	res2:2,
 		urg:1,
 		ack:1,
diff -u --recursive --new-file v1.1.85/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
--- v1.1.85/linux/include/linux/umsdos_fs.h	Mon Dec 12 21:15:19 1994
+++ linux/include/linux/umsdos_fs.h	Wed Jan 25 09:25:39 1995
@@ -2,7 +2,7 @@
 #define LINUX_UMSDOS_FS_H
 
 #define UMSDOS_VERSION	0
-#define UMSDOS_RELEASE	3
+#define UMSDOS_RELEASE	4
 
 #ifndef LINUX_FS_H
 #include <linux/fs.h>
@@ -78,6 +78,9 @@
 #define UMSDOS_GETVERSION	1241	/* Get the release number of UMSDOS */
 #define UMSDOS_INIT_EMD		1242	/* Create the EMD file if not there */
 #define UMSDOS_DOS_SETUP	1243	/* Set the defaults of the MsDOS driver */
+
+#define UMSDOS_RENAME_DOS	1244	/* rename a file/directory in the DOS */
+									/* directory only */
 
 struct umsdos_ioctl{
 	struct dirent dos_dirent;
diff -u --recursive --new-file v1.1.85/linux/ipc/shm.c linux/ipc/shm.c
--- v1.1.85/linux/ipc/shm.c	Thu Jan 26 07:49:16 1995
+++ linux/ipc/shm.c	Wed Jan 25 09:08:15 1995
@@ -463,7 +463,7 @@
 			if (!(page_table = (pte_t *) get_free_page(GFP_KERNEL)))
 				return -ENOMEM;
 			pgd_set(page_dir, page_table);
-			tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE);
+			tmp |= (PGDIR_SIZE - PAGE_SIZE);
 		}
 	}
 
@@ -669,14 +669,14 @@
 		}
 		pte_val(pte) = shp->shm_pages[idx];
 		if (pte_present(pte)) {
-			free_page (page);
+			free_page (page); /* doesn't sleep */
 			goto done;
 		}
 		if (!pte_none(pte)) {
 			read_swap_page(pte_val(pte), (char *) page);
 			pte_val(pte) = shp->shm_pages[idx];
 			if (pte_present(pte))  {
-				free_page (page);
+				free_page (page); /* doesn't sleep */
 				goto done;
 			}
 			swap_free(pte_val(pte));
@@ -688,7 +688,7 @@
 	} else
 		--current->mm->maj_flt;  /* was incremented in do_no_page */
 
-done:
+done:	/* pte_val(pte) == shp->shm_pages[idx] */
 	current->mm->min_flt++;
 	mem_map[MAP_NR(pte_page(pte))]++;
 	return pte_modify(pte, shmd->vm_page_prot);
@@ -743,7 +743,8 @@
 		swap_free (swap_nr);
 		return 0;
 	}
-	for (shmd = shp->attaches; ; ) {
+	if (shp->attaches)
+	  for (shmd = shp->attaches; ; ) {
 	    do {
 		pgd_t *page_dir;
 		pte_t *page_table, pte;
diff -u --recursive --new-file v1.1.85/linux/kernel/fork.c linux/kernel/fork.c
--- v1.1.85/linux/kernel/fork.c	Sun Jan 22 21:39:45 1995
+++ linux/kernel/fork.c	Fri Jan 27 12:20:29 1995
@@ -24,11 +24,6 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-/* These should maybe be in <linux/tasks.h> */
-
-#define MAX_TASKS_PER_USER (NR_TASKS/2)
-#define MIN_TASKS_LEFT_FOR_ROOT 4
-
 long last_pid=0;
 
 static int find_empty_process(void)
@@ -57,7 +52,7 @@
 			goto repeat;
 	}
 	if (tasks_free <= MIN_TASKS_LEFT_FOR_ROOT ||
-	    this_user_tasks > MAX_TASKS_PER_USER)
+	    this_user_tasks > current->rlim[RLIMIT_NPROC].rlim_cur)
 		if (current->uid)
 			return -EAGAIN;
 	return free_task;
diff -u --recursive --new-file v1.1.85/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.1.85/linux/kernel/ksyms.c	Thu Jan 26 07:49:16 1995
+++ linux/kernel/ksyms.c	Tue Jan 24 15:23:38 1995
@@ -54,7 +54,6 @@
 
 #ifdef CONFIG_FTAPE
 extern char * ftape_big_buffer;
-extern void (*do_floppy)(void);
 #endif
 
 #ifdef CONFIG_SCSI
@@ -256,7 +255,6 @@
 #ifdef CONFIG_FTAPE
 	/* The next labels are needed for ftape driver.  */
 	X(ftape_big_buffer),
-	X(do_floppy),
 #endif
 	X(floppy_track_buffer),
 #ifdef CONFIG_INET
diff -u --recursive --new-file v1.1.85/linux/kernel/sys.c linux/kernel/sys.c
--- v1.1.85/linux/kernel/sys.c	Sun Jan 22 21:39:45 1995
+++ linux/kernel/sys.c	Fri Jan 27 11:58:48 1995
@@ -700,6 +700,10 @@
 	     (new_rlim.rlim_max > old_rlim->rlim_max)) &&
 	    !suser())
 		return -EPERM;
+	if (resource == RLIMIT_NOFILE) {
+		if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN)
+			return -EPERM;
+	}
 	*old_rlim = new_rlim;
 	return 0;
 }
diff -u --recursive --new-file v1.1.85/linux/mm/swap.c linux/mm/swap.c
--- v1.1.85/linux/mm/swap.c	Sun Jan 22 23:04:00 1995
+++ linux/mm/swap.c	Wed Jan 25 09:08:15 1995
@@ -298,6 +298,8 @@
 		free_page(page);
 		return;
 	}
+	vma->vm_task->mm->rss++;
+	vma->vm_task->mm->maj_flt++;
 	if (!write_access && add_to_swap_cache(page, entry)) {
 		*page_table = mk_pte(page, vma->vm_page_prot);
 		return;
@@ -554,6 +556,10 @@
  *
  * With the above two rules, you get a straight-line execution path
  * for the normal case, giving better asm-code.
+ *
+ * free_page() may sleep since the page being freed may be a buffer
+ * page or present in the swap cache. It will not sleep, however,
+ * for a freshly allocated page (get_free_page()).
  */
 
 /*
diff -u --recursive --new-file v1.1.85/linux/net/inet/Makefile linux/net/inet/Makefile
--- v1.1.85/linux/net/inet/Makefile	Mon Jan 16 14:18:33 1995
+++ linux/net/inet/Makefile	Tue Jan 24 15:27:55 1995
@@ -39,14 +39,17 @@
 ifdef CONFIG_IPX
 
 OBJS	:= $(OBJS) ipx.o pe2.o p8022.o p8023.o
+SNAPO	:= psnap.o
 
 endif
 
-ifdef CONFIG_APPLETALK
-
-OBJS	:= $(OBJS) atalk_ddp.o atalk_arp.o
+ifdef CONFIG_ATALK
 
+OBJS	:= $(OBJS) aarp.o ddp.o
+SNAPO	:= psnap.o
 endif
+
+OBJS	:= $(OBJS) $(SNAPO)
 
 ifdef CONFIG_NET
 
diff -u --recursive --new-file v1.1.85/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.85/linux/net/inet/arp.c	Thu Jan 26 07:49:16 1995
+++ linux/net/inet/arp.c	Fri Jan 27 11:12:53 1995
@@ -581,9 +581,7 @@
 			break;
 #endif
 		case ARPHRD_ETHER:
-#ifdef CONFIG_ARCNET
 		case ARPHRD_ARCNET:
-#endif
 			if(arp->ar_pro != htons(ETH_P_IP))
 			{
 				kfree_skb(skb, FREE_READ);
@@ -659,7 +657,10 @@
 /* 
  * 	It is now an arp request 
  */
-		if(addr_hint != IS_MYADDR)
+/*
+ * Only reply for the real device address or when it's in our proxy tables
+ */
+		if(tip!=dev->pa_addr)
 		{
 /*
  * 	To get in here, it is a request for someone else.  We need to
@@ -704,8 +705,7 @@
 /*
  * 	To get here, it must be an arp request for us.  We need to reply.
  */
- 			if(tip==dev->pa_addr)	/* Only reply for the real device address */
-				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);
 		}
 	}
 
@@ -1057,12 +1057,12 @@
 			htype = ARPHRD_ETHER;
 			hlen = ETH_ALEN;
 			break;
-#ifdef CONFIG_ARCNET
+
 		case ARPHRD_ARCNET:
 			htype = ARPHRD_ARCNET;
 			hlen = 1;	/* length of arcnet addresses */
 			break;
-#endif
+
 #ifdef CONFIG_AX25
 		case ARPHRD_AX25:
 			htype = ARPHRD_AX25;
@@ -1099,6 +1099,13 @@
 	 *	Find the entry
 	 */
 	entry = arp_lookup(ip, 1);
+	if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL))
+	{
+		sti();
+		arp_destroy(ip,1);
+		cli();
+		entry = NULL;
+	}
 
 	/*
 	 *	Do we need to create a new entry
diff -u --recursive --new-file v1.1.85/linux/net/inet/datalink.h linux/net/inet/datalink.h
--- v1.1.85/linux/net/inet/datalink.h	Mon May 23 08:17:46 1994
+++ linux/net/inet/datalink.h	Tue Jan 24 15:27:55 1995
@@ -4,7 +4,7 @@
 struct datalink_proto {
 	unsigned short	type_len;
 	unsigned char	type[8];
-	unsigned short	datalink_type;
+	char		*string_name;
 	unsigned short	header_length;
 	int	(*rcvfunc)(struct sk_buff *, struct device *, 
 				struct packet_type *);
diff -u --recursive --new-file v1.1.85/linux/net/inet/eth.c linux/net/inet/eth.c
--- v1.1.85/linux/net/inet/eth.c	Mon Jan  9 07:22:13 1995
+++ linux/net/inet/eth.c	Tue Jan 24 15:27:55 1995
@@ -191,8 +191,6 @@
 	
 	if (*(unsigned short *)rawp == 0xFFFF)
 		return htons(ETH_P_802_3);
-	if (*(unsigned short *)rawp == 0xAAAA)
-		return htons(ETH_P_SNAP);
 		
 	return htons(ETH_P_802_2);
 }
diff -u --recursive --new-file v1.1.85/linux/net/inet/icmp.c linux/net/inet/icmp.c
--- v1.1.85/linux/net/inet/icmp.c	Mon Dec 12 21:01:10 1994
+++ linux/net/inet/icmp.c	Thu Jan 26 07:25:34 1995
@@ -11,6 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Mark Evans, <evansmp@uhura.aston.ac.uk>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Stefan Becker, <stefanb@yello.ping.de>
  *
  * Fixes:	
  *		Alan Cox	:	Generic queue usage.
@@ -28,6 +29,7 @@
  *		Laco Rusnak	:	Multihoming fixes.
  *		Alan Cox	:	Tightened up icmp_send().
  *		Alan Cox	:	Multicasts.
+ *		Stefan Becker   :       ICMP redirects in icmp_send().
  *
  * 
  *
@@ -94,7 +96,7 @@
  *	Fixme: Fragment handling is wrong really.
  */
  
-void icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
+void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
 {
 	struct sk_buff *skb;
 	struct iphdr *iph;
@@ -139,8 +141,29 @@
 	 
 	if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)
 	{
+
+		/*
+		 *	Is the original packet an ICMP packet?
+		 */
+
 		if(iph->protocol==IPPROTO_ICMP)
-			return;
+		{
+			icmph = (struct icmphdr *) ((char *) iph +
+                                                    4 * iph->ihl);
+			/*
+			 *	Check for ICMP error packets (Must never reply to
+			 *	an ICMP error).
+			 */
+	
+			if (icmph->type == ICMP_DEST_UNREACH ||
+				icmph->type == ICMP_SOURCE_QUENCH ||
+				icmph->type == ICMP_REDIRECT ||
+				icmph->type == ICMP_TIME_EXCEEDED ||
+				icmph->type == ICMP_PARAMETERPROB)
+				return;
+		}
+  
+		return;
 
 	}
 	icmp_statistics.IcmpOutMsgs++;
@@ -232,7 +255,9 @@
 	icmph->type = type;
 	icmph->code = code;
 	icmph->checksum = 0;
-	icmph->un.gateway = 0;
+	icmph->un.gateway = info;	/* This might not be meant for 
+					   this form of the union but it will
+					   be right anyway */
 	memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
 
 	icmph->checksum = ip_compute_csum((unsigned char *)icmph,
diff -u --recursive --new-file v1.1.85/linux/net/inet/icmp.h linux/net/inet/icmp.h
--- v1.1.85/linux/net/inet/icmp.h	Mon Apr 18 11:38:47 1994
+++ linux/net/inet/icmp.h	Thu Jan 26 07:25:41 1995
@@ -26,7 +26,7 @@
 
 
 extern void	icmp_send(struct sk_buff *skb_in,  int type, int code,
-			  struct device *dev);
+			  unsigned long info, struct device *dev);
 extern int	icmp_rcv(struct sk_buff *skb1, struct device *dev,
 			 struct options *opt, unsigned long daddr,
 			 unsigned short len, unsigned long saddr,
diff -u --recursive --new-file v1.1.85/linux/net/inet/igmp.c linux/net/inet/igmp.c
--- v1.1.85/linux/net/inet/igmp.c	Mon Jan  9 07:22:13 1995
+++ linux/net/inet/igmp.c	Tue Jan 24 15:31:48 1995
@@ -32,7 +32,7 @@
 #include "route.h"
 #include <linux/skbuff.h>
 #include "sock.h"
-#include "igmp.h"
+#include <linux/igmp.h>
 
 #ifdef CONFIG_IP_MULTICAST
 
diff -u --recursive --new-file v1.1.85/linux/net/inet/igmp.h linux/net/inet/igmp.h
--- v1.1.85/linux/net/inet/igmp.h	Fri Dec  2 11:18:59 1994
+++ linux/net/inet/igmp.h	Thu Jan  1 02:00:00 1970
@@ -1,70 +0,0 @@
-/*
- *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
- *
- *	Authors:
- *		Alan Cox <Alan.Cox@linux.org>	
- *
- *	WARNING:
- *		This is a 'preliminary' implementation... on your own head
- *	be it.
- *
- *	This program is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU General Public License
- *	as published by the Free Software Foundation; either version
- *	2 of the License, or (at your option) any later version.
- */
-
-#ifndef _IGMP_H
-#define _IGMP_H
-
-/*
- *	IGMP protocol structures
- */
-
-struct igmphdr
-{
-	unsigned char type;
-	unsigned char unused;
-	unsigned short csum;
-	unsigned long group;
-};
-
-#define IGMP_HOST_MEMBERSHIP_QUERY	0x11	/* From RFC1112 */
-#define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
-#define IGMP_HOST_LEAVE_MESSAGE		0x17	/* An extra BSD seems to send */
-
-				/* 224.0.0.1 */
-#define IGMP_ALL_HOSTS		htonl(0xE0000001L)
-
-/*
- * struct for keeping the multicast list in
- */
-
-struct ip_mc_socklist
-{
-	unsigned long multiaddr[IP_MAX_MEMBERSHIPS];	/* This is a speed trade off */
-	struct device *multidev[IP_MAX_MEMBERSHIPS];
-};
-
-struct ip_mc_list 
-{
-	struct device *interface;
-	unsigned long multiaddr;
-	struct ip_mc_list *next;
-	struct timer_list timer;
-	int tm_running;
-	int users;
-};
- 
-extern struct ip_mc_list *ip_mc_head;
-
-
-#ifdef __KERNEL__
-extern int igmp_rcv(struct sk_buff *, struct device *, struct options *, unsigned long, unsigned short,
-	unsigned long, int , struct inet_protocol *);
-extern void ip_mc_drop_device(struct device *dev); 
-extern int ip_mc_join_group(struct sock *sk, struct device *dev, unsigned long addr);
-extern int ip_mc_leave_group(struct sock *sk, struct device *dev,unsigned long addr);
-extern void ip_mc_drop_socket(struct sock *sk);
-#endif
-#endif
diff -u --recursive --new-file v1.1.85/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.1.85/linux/net/inet/ip.c	Thu Jan 26 07:49:17 1995
+++ linux/net/inet/ip.c	Thu Jan 26 07:26:48 1995
@@ -12,6 +12,8 @@
  *		Donald Becker, <becker@super.org>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *		Richard Underwood
+ *		Stefan Becker, <stefanb@yello.ping.de>
+ *		
  *
  * Fixes:
  *		Alan Cox	:	Commented a couple of minor bits of surplus code
@@ -65,6 +67,8 @@
  *		Alan Cox	:	Use notifiers.
  *		Bjorn Ekwall	:	Removed ip_csum (from slhc.c too)
  *		Bjorn Ekwall	:	Moved ip_fast_csum to ip.h (inline!)
+ *		Stefan Becker   :       Send out ICMP HOST REDIRECT
+ *  
  *
  * To Fix:
  *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
@@ -110,7 +114,7 @@
 #include "arp.h"
 #include "icmp.h"
 #include "raw.h"
-#include "igmp.h"
+#include <linux/igmp.h>
 #include <linux/ip_fw.h>
 
 #define CONFIG_IP_DEFRAG
@@ -688,7 +692,7 @@
 	/* This if is always true... shrug */
 	if(qp->fragments!=NULL)
 		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
-				ICMP_EXC_FRAGTIME, qp->dev);
+				ICMP_EXC_FRAGTIME, 0, qp->dev);
 
 	/*
 	 *	Nuke the fragment queue.
@@ -1113,8 +1117,11 @@
 
 	if (ntohs(iph->frag_off) & IP_DF)
 	{
+		/*
+		 *	Reply giving the MTU of the failed hop.
+		 */
 		ip_statistics.IpFragFails++;
-		icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev);
+		icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev);
 		return;
 	}
 
@@ -1127,7 +1134,7 @@
 	if(mtu<8)
 	{
 		/* It's wrong but its better than nothing */
-		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev);
+		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);
 		ip_statistics.IpFragFails++;
 		return;
 	}
@@ -1282,7 +1289,7 @@
 	if (iph->ttl <= 0)
 	{
 		/* Tell the sender its packet died... */
-		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
+		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
 		return;
 	}
 
@@ -1306,7 +1313,7 @@
 		 *	Tell the sender its packet cannot be delivered. Again
 		 *	ICMP is screened later.
 		 */
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
 		return;
 	}
 
@@ -1333,7 +1340,7 @@
 			/*
 			 *	Tell the sender its packet cannot be delivered...
 			 */
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
 			return;
 		}
 		if (rt->rt_gateway != 0)
@@ -1349,14 +1356,17 @@
 	dev2 = rt->rt_dev;
 
 	/*
-	 *	In IP you never forward a frame on the interface that it arrived
-	 *	upon. We should generate an ICMP HOST REDIRECT giving the route
+	 *	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
 	 *	we calculated.
-	 *	For now just dropping the packet is an acceptable compromise.
 	 */
-
+#ifdef IP_NO_ICMP_REDIRECT
 	if (dev == dev2)
 		return;
+#else
+	if (dev == dev2)
+		icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
+#endif		
 
 	/*
 	 * We now allocate a new buffer, and copy the datagram into it.
@@ -1708,7 +1718,7 @@
 	else if (!flag)		/* Free and report errors */
 	{
 		if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);
 		kfree_skb(skb, FREE_WRITE);
 	}
 
diff -u --recursive --new-file v1.1.85/linux/net/inet/ipx.c linux/net/inet/ipx.c
--- v1.1.85/linux/net/inet/ipx.c	Wed Jan 11 21:14:29 1995
+++ linux/net/inet/ipx.c	Tue Jan 24 15:27:55 1995
@@ -33,6 +33,11 @@
  *			Asynchronous I/O support.
  *			Changed to use notifiers and the newer packet_type stuff.
  *			Assorted major fixes <Alejandro Liu>
+ *
+ * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
+ *	Neither Greg Page nor Caldera, Inc. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.		
  */
   
 #include <linux/config.h>
@@ -58,68 +63,98 @@
 #include <linux/termios.h>	/* For TIOCOUTQ/INQ */
 #include <linux/interrupt.h>
 #include "p8022.h"
+#include "psnap.h"
 
 #ifdef CONFIG_IPX
+/* Configuration Variables */
+static unsigned char	ipxcfg_max_hops = 16;
+static char	ipxcfg_auto_select_primary = 0;
+static char	ipxcfg_auto_create_interfaces = 0;
+
+/* Global Variables */
+static struct datalink_proto	*p8022_datalink = NULL;
+static struct datalink_proto	*pEII_datalink = NULL;
+static struct datalink_proto	*p8023_datalink = NULL;
+static struct datalink_proto	*pSNAP_datalink = NULL;
+
+static ipx_interface	*ipx_interfaces = NULL;
+static ipx_route 	*ipx_routes = NULL;
+static ipx_interface	*ipx_internal_net = NULL;
+static ipx_interface	*ipx_primary_net = NULL;
+
+static int
+ipxcfg_set_auto_create(char val)
+{
+	ipxcfg_auto_create_interfaces = val;
+	return 0;
+}
+		
+static int
+ipxcfg_set_auto_select(char val)
+{
+	ipxcfg_auto_select_primary = val;
+	if (val && (ipx_primary_net == NULL))
+		ipx_primary_net = ipx_interfaces;
+	return 0;
+}
+
+static int
+ipxcfg_get_config_data(ipx_config_data *arg)
+{
+	ipx_config_data	vals;
+	
+	vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
+	vals.ipxcfg_auto_select_primary = ipxcfg_auto_select_primary;
+	memcpy_tofs(arg, &vals, sizeof(vals));
+	return 0;
+}
+
+
 /***********************************************************************************************************************\
 *															*
 *						Handlers for the socket list.						*
 *															*
 \***********************************************************************************************************************/
 
-static ipx_socket *volatile ipx_socket_list=NULL;
-
 /*
  *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
  *	handler using this technique. They can be added although we do not
  *	use this facility.
  */
  
-static void ipx_remove_socket(ipx_socket *sk)
+static void 
+ipx_remove_socket(ipx_socket *sk)
 {
-	ipx_socket *s;
-	
+	ipx_socket	*s;
+	ipx_interface	*intrfc;
+	unsigned long	flags;
+
+	save_flags(flags);
 	cli();
-	s=ipx_socket_list;
-	if(s==sk)
-	{
-		ipx_socket_list=s->next;
-		sti();
+	
+	/* Determine interface with which socket is associated */
+	intrfc = sk->ipx_intrfc;
+	if (intrfc == NULL) {
+		restore_flags(flags);
 		return;
 	}
-	while(s && s->next)
-	{
-		if(s->next==sk)
-		{
-			s->next=sk->next;
-			sti();
-			return;
-		}
-		s=s->next;
-	}
-	sti();
-}
 
-static void ipx_insert_socket(ipx_socket *sk)
-{
-	cli();
-	sk->next=ipx_socket_list;
-	ipx_socket_list=sk;
-	sti();
-}
+	s=intrfc->if_sklist;
+	if(s==sk) {
+		intrfc->if_sklist=s->next;
+		restore_flags(flags);
+		return;
+	} 
 
-static ipx_socket *ipx_find_socket(int port)
-{
-	ipx_socket *s;
-	s=ipx_socket_list;
-	while(s)
-	{
-		if(s->ipx_source_addr.sock==port)
-		{
-			return(s);
+	while(s && s->next) {
+		if(s->next==sk) {
+			s->next=sk->next;
+			restore_flags(flags);
+			return;
 		}
 		s=s->next;
 	}
-	return(NULL);
+	restore_flags(flags);
 }
 
 /*
@@ -129,308 +164,1028 @@
  *	touch it and we are (fairly 8-) ) safe.
  */
  
-static void ipx_destroy_socket(ipx_socket *sk)
+static void 
+ipx_destroy_socket(ipx_socket *sk)
 {
-	struct sk_buff *skb;
+	struct sk_buff	*skb;
+
 	ipx_remove_socket(sk);
-	
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
-	{
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
 		kfree_skb(skb,FREE_READ);
 	}
 	
 	kfree_s(sk,sizeof(*sk));
 }
+	
+/* The following code is used to support IPX Interfaces (IPXITF).  An
+ * IPX interface is defined by a physical device and a frame type.
+ */
 
+static ipx_route * ipxrtr_lookup(unsigned long);
+ 
+static void
+ipxitf_clear_primary_net(void)
+{
+	if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL))
+		ipx_primary_net = ipx_interfaces;
+	else
+		ipx_primary_net = NULL;
+}
 
-/* Called from proc fs */
-int ipx_get_info(char *buffer, char **start, off_t offset, int length)
+static ipx_interface *
+ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
 {
-	ipx_socket *s;
-	int len=0;
-	off_t pos=0;
-	off_t begin=0;
+	ipx_interface	*i;
 
-	/* Theory.. Keep printing in the same place until we pass offset */
-	
-	len += sprintf (buffer,"Type local_address             rem_address              tx_queue rx_queue st uid\n");
-	for (s = ipx_socket_list; s != NULL; s = s->next)
-	{
-		len += sprintf (buffer+len,"%02X   ", s->ipx_type);
-		len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->ipx_source_addr.net),
-			s->ipx_source_addr.node[0], s->ipx_source_addr.node[1], s->ipx_source_addr.node[2],
-			s->ipx_source_addr.node[3], s->ipx_source_addr.node[4], s->ipx_source_addr.node[5],
-			htons(s->ipx_source_addr.sock));
-		len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->ipx_dest_addr.net),
-			s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], s->ipx_dest_addr.node[2],
-			s->ipx_dest_addr.node[3], s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
-			htons(s->ipx_dest_addr.sock));
-		len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc);
-		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
-		
-		/* Are we still dumping unwanted data then discard the record */
-		pos=begin+len;
-		
-		if(pos<offset)
-		{
-			len=0;			/* Keep dumping into the buffer start */
-			begin=pos;
-		}
-		if(pos>offset+length)		/* We have dumped enough */
-			break;
+	for (i=ipx_interfaces; 
+		i && ((i->if_dev!=dev) || (i->if_dlink_type!=datalink)); 
+		i=i->if_next)
+		;
+	return i;
+}
+
+static ipx_interface *
+ipxitf_find_using_net(unsigned long net)
+{
+	ipx_interface	*i;
+
+	if (net == 0L)
+		return ipx_primary_net;
+
+	for (i=ipx_interfaces; i && (i->if_netnum!=net); i=i->if_next)
+		;
+
+	return i;
+}
+
+/* Sockets are bound to a particular IPX interface. */
+static void
+ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
+{
+	ipx_socket	*s;
+
+	sk->ipx_intrfc = intrfc;
+	sk->next = NULL;
+	if (intrfc->if_sklist == NULL) {
+		intrfc->if_sklist = sk;
+	} else {
+		for (s = intrfc->if_sklist; s->next != NULL; s = s->next)
+			;
+		s->next = sk;
 	}
-	
-	/* The data in question runs from begin to begin+len */
-	*start=buffer+(offset-begin);	/* Start of wanted data */
-	len-=(offset-begin);		/* Remove unwanted header data from length */
-	if(len>length)
-		len=length;		/* Remove unwanted tail data from length */
-	
-	return len;
 }
 
-/*******************************************************************************************************************\
-*													            *
-*	            			Routing tables for the IPX socket layer				            *
-*														    *
-\*******************************************************************************************************************/
+static ipx_socket *
+ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+{
+	ipx_socket	*s;
 
+	for (s=intrfc->if_sklist; 
+		(s != NULL) && (s->ipx_port != port); 
+		s=s->next)
+		;
 
-static struct datalink_proto	*p8022_datalink = NULL;
-static struct datalink_proto	*pEII_datalink = NULL;
-static struct datalink_proto	*p8023_datalink = NULL;
-static struct datalink_proto	*pSNAP_datalink = NULL;
+	return s;
+}
 
-static ipx_route *ipx_router_list=NULL;
-static ipx_route *ipx_localnet_list=NULL;
+static void ipxrtr_del_routes(ipx_interface *);
 
-static ipx_route *
-ipxrtr_get_local_net(struct device *dev, unsigned short datalink)
+static void
+ipxitf_down(ipx_interface *intrfc)
 {
-	ipx_route *r;
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	r=ipx_localnet_list;
-	while(r!=NULL)
-	{
-		if((r->dev==dev) && (r->dlink_type == datalink))
-		{
-			restore_flags(flags);
-			return r;
-		}
-		r=r->nextlocal;
+	ipx_interface	*i;
+	ipx_socket	*s, *t;
+
+	/* Delete all routes associated with this interface */
+	ipxrtr_del_routes(intrfc);
+
+	/* error sockets */
+	for (s = intrfc->if_sklist; s != NULL; ) {
+		s->err = ENOLINK;
+		s->error_report(s);
+		s->ipx_intrfc = NULL;
+		s->ipx_port = 0;
+		s->zapped=1;	/* Indicates it is no longer bound */
+		t = s;
+		s = s->next;
+		t->next = NULL;
 	}
-	restore_flags(flags);
-	return NULL;
+	intrfc->if_sklist = NULL;
+
+	/* remove this interface from list */
+	if (intrfc == ipx_interfaces) {
+		ipx_interfaces = intrfc->if_next;
+	} else {
+		for (i = ipx_interfaces; 
+			(i != NULL) && (i->if_next != intrfc);
+			i = i->if_next)
+			;
+		if ((i != NULL) && (i->if_next == intrfc)) 
+			i->if_next = intrfc->if_next;
+	}
+
+	/* remove this interface from *special* networks */
+	if (intrfc == ipx_primary_net)
+		ipxitf_clear_primary_net();
+	if (intrfc == ipx_internal_net)
+		ipx_internal_net = NULL;
+
+	kfree_s(intrfc, sizeof(*intrfc));
 }
+
+static int 
+ipxitf_device_event(unsigned long event, void *ptr)
+{
+	struct device *dev = ptr;
+	ipx_interface *i, *tmp;
+
+	if(event!=NETDEV_DOWN)
+		return NOTIFY_DONE;
+
+	for (i = ipx_interfaces; i != NULL; ) {
 	
-static ipx_route *
-ipxrtr_get_default_net(void)
+		tmp = i->if_next;
+		if (i->if_dev == dev) 
+			ipxitf_down(i);
+		i = tmp;
+
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int
+ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
 {
-	return ipx_localnet_list;
+	int	retval;
+
+	if((retval = sock_queue_rcv_skb(sock, skb))<0) {
+		/*
+	 	 *	We do a FREE_WRITE here because this indicates how
+	 	 *	to treat the socket with which the packet is 
+	 	 *	associated.  If this packet is associated with a
+	 	 *	socket at all, it must be the originator of the 
+	 	 *	packet.   Incoming packets will have no socket 
+		 *	associated with them at this point.
+	 	 */
+		kfree_skb(skb,FREE_WRITE);
+	}
+	return retval;
+}
+
+static int
+ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
+{
+	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
+	ipx_socket	*sock1 = NULL, *sock2 = NULL;
+	struct sk_buff	*skb1 = NULL, *skb2 = NULL;
+	int		ipx_offset;
+
+	sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+
+	/*
+	 *	We need to check if there is a primary net and if
+	 *	this is addressed to one of the *SPECIAL* sockets because
+	 *	these need to be propogated to the primary net.
+	 *	The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
+	 *	0x456(Diagnostic).
+	 */
+	if (ipx_primary_net && (intrfc != ipx_primary_net)) {
+		switch (ntohs(ipx->ipx_dest.sock)) {
+		case 0x452:
+		case 0x453:
+		case 0x456:
+			/*
+			 *	The appropriate thing to do here is to
+			 * 	dup the packet and route to the primary net
+			 *	interface via ipxitf_send; however, we'll cheat
+			 *	and just demux it here.
+			 */
+			sock2 = ipxitf_find_socket(ipx_primary_net, 
+					ipx->ipx_dest.sock);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* if there is nothing to do, return */
+	if ((sock1 == NULL) && (sock2 == NULL)) {
+		if (!copy) 
+			kfree_skb(skb,FREE_WRITE);
+		return 0;
+	}
+
+	ipx_offset = (char *)(skb->h.raw) - (char *)(skb->data);
+
+	/* This next segment of code is a little awkward, but it sets it up
+	 * so that the appropriate number of copies of the SKB are made and 
+	 * that skb1 and skb2 point to it (them) so that it (they) can be 
+	 * demuxed to sock1 and/or sock2.  If we are unable to make enough
+	 * copies, we do as much as is possible.
+	 */
+	if (copy) {
+		skb1 = skb_clone(skb, GFP_ATOMIC);
+		if (skb1 != NULL) {
+			skb1->h.raw = (unsigned char *)&(skb1->data[ipx_offset]);
+			skb1->arp = skb1->free = 1;
+		}
+	} else {
+		skb1 = skb;
+	}
+	
+	if (skb1 == NULL) return -ENOMEM; 
+
+	/* Do we need 2 SKBs? */
+	if (sock1 && sock2) {
+		skb2 = skb_clone(skb1, GFP_ATOMIC);
+		if (skb2 != NULL) {
+			skb2->h.raw = (unsigned char *)&(skb2->data[ipx_offset]);
+			skb2->arp = skb2->free = 1;
+		}
+	} else {
+		skb2 = skb1;
+	}
+		
+	if (sock1) {
+		(void) ipxitf_def_skb_handler(sock1, skb1);
+	}
+
+	if (skb2 == NULL) return -ENOMEM;
+
+	if (sock2) {
+		(void) ipxitf_def_skb_handler(sock2, skb2);
+	}
+
+	return 0;
 }
 
-static ipx_route *ipxrtr_get_dev(long net)
+static struct sk_buff *
+ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
 {
-	ipx_route *r;
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	r=ipx_router_list;
-	while(r!=NULL)
-	{
-		if(r->net==net)
-		{
-			restore_flags(flags);
-			return r;
+	struct sk_buff	*skb2;
+	int	in_offset = skb->h.raw - skb->data;
+	int	out_offset = intrfc->if_ipx_offset;
+	char	*oldraw;
+	int	len;
+
+	/* Hopefully, most cases */
+	if (in_offset == out_offset) {
+		return skb;
+	}
+
+	/* Existing SKB will work, just need to move things around a little */
+	if (in_offset > out_offset) {
+		oldraw = skb->h.raw;
+		skb->h.raw = &(skb->data[out_offset]);
+		memmove(skb->h.raw, oldraw, skb->len);
+		skb->len += out_offset;
+		return skb;
+	}
+
+	/* Need new SKB */
+	len = skb->len + out_offset;
+	skb2 = alloc_skb(len, GFP_ATOMIC);
+	if (skb2 != NULL) {
+		skb2->h.raw = &(skb2->data[out_offset]);
+		skb2->len = len;
+		skb2->free=1;
+		skb2->arp=1;
+		memcpy(skb2->h.raw, skb->h.raw, skb->len);
+	}
+	kfree_skb(skb, FREE_WRITE);
+	return skb2;
+}
+
+static int
+ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
+{
+	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
+	struct device	*dev = intrfc->if_dev;
+	struct datalink_proto	*dl = intrfc->if_dlink;
+	char		dest_node[IPX_NODE_LEN];
+	int		send_to_wire = 1;
+	
+	/* We need to know how many skbuffs it will take to send out this
+	 * packet to avoid unnecessary copies.
+	 */
+	if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK)) 
+		send_to_wire = 0;
+
+	/* See if this should be demuxed to sockets on this interface */
+	if (ipx->ipx_dest.net == intrfc->if_netnum) {
+		if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) 
+			return ipxitf_demux_socket(intrfc, skb, 0);
+		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+			ipxitf_demux_socket(intrfc, skb, send_to_wire);
+			if (!send_to_wire) return 0;
 		}
-		r=r->next;
 	}
-	restore_flags(flags);
-	return NULL;
+
+	/* if the orginating net is not equal to our net; this is routed */
+	if (ipx->ipx_source.net != intrfc->if_netnum) {
+		if (++(ipx->ipx_tctrl) > ipxcfg_max_hops) 
+			send_to_wire = 0;
+	}
+
+	if (!send_to_wire) {
+		/*
+		 *	We do a FREE_WRITE here because this indicates how
+		 *	to treat the socket with which the packet is 
+	 	 *	associated.  If this packet is associated with a
+		 *	socket at all, it must be the originator of the 
+		 *	packet.   Routed packets will have no socket associated
+		 *	with them.
+		 */
+		kfree_skb(skb,FREE_WRITE);
+		return 0;
+	}
+
+	/* In some case, ipxitf_adjust_skbuff can overwrite node */
+	memcpy(dest_node, node, IPX_NODE_LEN);
+
+	/* make any compensation for differing physical/data link size */
+	skb = ipxitf_adjust_skbuff(intrfc, skb);
+	if (skb == NULL) return 0;
+
+	/* set up data link and physical headers */
+	skb->dev = dev;
+	dl->datalink_header(dl, skb, dest_node);
+
+	if (skb->sk != NULL) {
+		/* This is an outbound packet from this host.  We need to 
+		 * increment the write count.
+		 */
+		skb->sk->wmem_alloc += skb->mem_len;
+	}
+
+	/* Send it out */
+	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	return 0;
 }
 
-static void ipxrtr_add_localnet(ipx_route *newnet)
+static int
+ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
+
+static int
+ipxitf_add_local_route(ipx_interface *intrfc)
 {
-	ipx_route *r;
-	unsigned long flags;
-	save_flags(flags);
-	cli();
+	return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
+}
 
-	newnet->nextlocal = NULL;
-	if (ipx_localnet_list == NULL) {
-		ipx_localnet_list = newnet;
-		restore_flags(flags);
-		return;
+static char * ipx_frame_name(unsigned short);
+static char * ipx_device_name(ipx_interface *);
+static int ipxrtr_route_skb(struct sk_buff *);
+
+static int 
+ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
+{
+	ipx_packet	*ipx = (ipx_packet *) (skb->h.raw);
+	ipx_interface	*i;
+
+	/* See if we should update our network number */
+	if ((intrfc->if_netnum == 0L) && (ipx->ipx_tctrl == 0)) {
+		if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) {
+			intrfc->if_netnum = ipx->ipx_source.net;
+			(void) ipxitf_add_local_route(intrfc);
+		} else {
+			printk("IPX: Network number collision %lx\n\t%s %s and %s %s\n",
+				htonl(ipx->ipx_source.net), 
+				ipx_device_name(i),
+				ipx_frame_name(i->if_dlink_type),
+				ipx_device_name(intrfc),
+				ipx_frame_name(intrfc->if_dlink_type));
+		}
 	}
 
-	r=ipx_localnet_list;
-	while(r->nextlocal!=NULL)
-		r=r->nextlocal;
+	if (intrfc->if_netnum != ipx->ipx_dest.net)
+		return ipxrtr_route_skb(skb);
 
-	r->nextlocal = newnet;
-	
-	restore_flags(flags);
-	return;
+	/* see if we should keep it */
+	if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0) 
+		|| (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)) {
+		return ipxitf_demux_socket(intrfc, skb, 0);
+	}
+
+	/* we couldn't pawn it off so unload it */
+	kfree_skb(skb,FREE_READ);
+	return 0;
 }
 
-static int ipxrtr_create(struct ipx_route_def *r)
+static void
+ipxitf_insert(ipx_interface *intrfc)
 {
-	ipx_route *rt=ipxrtr_get_dev(r->ipx_network);
-	struct device *dev;
-	unsigned short	dlink_type;
-	struct datalink_proto *datalink = NULL;
+	ipx_interface	*i;
+
+	intrfc->if_next = NULL;
+	if (ipx_interfaces == NULL) {
+		ipx_interfaces = intrfc;
+	} else {
+		for (i = ipx_interfaces; i->if_next != NULL; i = i->if_next)
+			;
+		i->if_next = intrfc;
+	}
+
+	if (ipxcfg_auto_select_primary && (ipx_primary_net == NULL))
+		ipx_primary_net = intrfc;
+}
+
+static int 
+ipxitf_create_internal(ipx_interface_definition *idef)
+{
+	ipx_interface	*intrfc;
+
+	/* Only one primary network allowed */
+	if (ipx_primary_net != NULL) return -EEXIST;
+
+	/* Must have a valid network number */
+	if (idef->ipx_network == 0L) return -EADDRNOTAVAIL;
+	if (ipxitf_find_using_net(idef->ipx_network) != NULL)
+		return -EADDRINUSE;
 
-	if (r->ipx_flags & IPX_RT_BLUEBOOK) {
+	intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+	if (intrfc==NULL)
+		return -EAGAIN;
+	intrfc->if_dev=NULL;
+	intrfc->if_netnum=idef->ipx_network;
+	intrfc->if_dlink_type = 0;
+	intrfc->if_dlink = NULL;
+	intrfc->if_sklist = NULL;
+	intrfc->if_internal = 1;
+	intrfc->if_ipx_offset = 0;
+	intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+	memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
+	ipx_internal_net = intrfc;
+	ipx_primary_net = intrfc;
+	ipxitf_insert(intrfc);
+	return ipxitf_add_local_route(intrfc);
+}
+
+static int 
+ipxitf_create(ipx_interface_definition *idef)
+{
+	struct device	*dev;
+	unsigned short	dlink_type = 0;
+	struct datalink_proto	*datalink = NULL;
+	ipx_interface	*intrfc;
+
+	if (idef->ipx_internal) 
+		return ipxitf_create_internal(idef);
+
+	if (idef->ipx_primary && (ipx_primary_net != NULL))
+		return -EEXIST;
+
+	if ((idef->ipx_network != 0L) &&
+		(ipxitf_find_using_net(idef->ipx_network) != NULL))
+		return -EADDRINUSE;
+
+	switch (idef->ipx_dlink_type) {
+	case IPX_FRAME_ETHERII: 
 		dlink_type = htons(ETH_P_IPX);
 		datalink = pEII_datalink;
-	} else if (r->ipx_flags & IPX_RT_8022) {
+		break;
+	case IPX_FRAME_8022:
 		dlink_type = htons(ETH_P_802_2);
 		datalink = p8022_datalink;
-	} else if (r->ipx_flags & IPX_RT_SNAP) {
+		break;
+	case IPX_FRAME_SNAP:
 		dlink_type = htons(ETH_P_SNAP);
 		datalink = pSNAP_datalink;
-	} else {
+		break;
+	case IPX_FRAME_8023:
 		dlink_type = htons(ETH_P_802_3);
 		datalink = p8023_datalink;
+		break;
+	case IPX_FRAME_NONE:
+	default:
+		break;
 	}
 
-	if (datalink == NULL) {
-		printk("IPX: Unsupported datalink protocol.\n");
+	if (datalink == NULL) 
 		return -EPROTONOSUPPORT;
-	}
 
-	if(r->ipx_router_network!=0)
-	{
-		/* Adding an indirect route */
-		ipx_route *rt1=ipxrtr_get_dev(r->ipx_router_network);
-		if(rt1==NULL)
-			return -ENETUNREACH;
-		if(rt1->flags&IPX_RT_ROUTED)
-			return -EMULTIHOP;
-		if (rt==NULL)
-		{
-			rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
-			if(rt==NULL)
-				return -EAGAIN;
-			rt->next=ipx_router_list;
-			ipx_router_list=rt;
-		}
-		rt->net=r->ipx_network;
-		rt->router_net=r->ipx_router_network;
-		memcpy(rt->router_node,r->ipx_router_node,sizeof(rt->router_node));
-		rt->flags=IPX_RT_ROUTED;
-		rt->dlink_type = dlink_type;
-		rt->datalink = datalink;
-		rt->dev=rt1->dev;
-		return 0;
-	}
-	/* Add a direct route */
-	dev=dev_get(r->ipx_device);
+	dev=dev_get(idef->ipx_device);
 	if(dev==NULL)
 		return -ENODEV;
+
 	/* Check addresses are suitable */
-	if(dev->addr_len>6)
+	if(dev->addr_len>IPX_NODE_LEN)
 		return -EINVAL;
+
 	if(dev->addr_len<2)
 		return -EINVAL;
-	if (ipxrtr_get_local_net(dev, dlink_type) != NULL)
-		return -EEXIST;
-	/* Ok now create */
-	rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
-	if(rt==NULL)
-		return -EAGAIN;
-	rt->next=ipx_router_list;
-	ipx_router_list=rt;
-	rt->router_net=0;
-	memset(rt->router_node,0,sizeof(rt->router_node));
-	rt->dev=dev;
-	rt->net=r->ipx_network;
-	rt->flags=0;
-	rt->dlink_type = dlink_type;
-	rt->datalink = datalink;
-	ipxrtr_add_localnet(rt);
-	return 0;
-}
 
+	if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) {
+
+		/* Ok now create */
+		intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+		if (intrfc==NULL)
+			return -EAGAIN;
+		intrfc->if_dev=dev;
+		intrfc->if_netnum=idef->ipx_network;
+		intrfc->if_dlink_type = dlink_type;
+		intrfc->if_dlink = datalink;
+		intrfc->if_sklist = NULL;
+		intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+		/* Setup primary if necessary */
+		if (idef->ipx_primary) ipx_primary_net = intrfc;
+		intrfc->if_internal = 0;
+		intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length;
+		memset(intrfc->if_node, 0, IPX_NODE_LEN);
+		memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len);
+
+		ipxitf_insert(intrfc);
+	}
+
+	/* If the network number is known, add a route */
+	if (intrfc->if_netnum == 0L) 
+		return 0;
+
+	return ipxitf_add_local_route(intrfc);
+}
 
-static int ipxrtr_delete_localnet(ipx_route *d)
+static int 
+ipxitf_delete(ipx_interface_definition *idef)
 {
-	ipx_route **r = &ipx_localnet_list;
-	ipx_route *tmp;
+	struct device	*dev = NULL;
+	unsigned short	dlink_type = 0;
+	ipx_interface	*intrfc;
 
-	while ((tmp = *r) != NULL) {
-		if (tmp == d) {
-			*r = tmp->next;
+	if (idef->ipx_internal) {
+		if (ipx_internal_net != NULL) {
+			ipxitf_down(ipx_internal_net);
 			return 0;
 		}
-		r = &tmp->nextlocal;
+		return -ENOENT;
 	}
-	return -ENOENT;
+
+	switch (idef->ipx_dlink_type) {
+	case IPX_FRAME_ETHERII: 
+		dlink_type = htons(ETH_P_IPX);
+		break;
+	case IPX_FRAME_8022:
+		dlink_type = htons(ETH_P_802_2);
+		break;
+	case IPX_FRAME_SNAP:
+		dlink_type = htons(ETH_P_SNAP);
+		break;
+	case IPX_FRAME_8023:
+		dlink_type = htons(ETH_P_802_3);
+		break;
+	case IPX_FRAME_NONE:
+	default:
+		return -EPROTONOSUPPORT;
+		break;
+	}
+
+	dev=dev_get(idef->ipx_device);
+	if(dev==NULL) return -ENODEV;
+
+	intrfc = ipxitf_find_using_phys(dev, dlink_type);
+	if (intrfc != NULL) {
+		ipxitf_down(intrfc);
+		return 0;
+	}
+	return -EINVAL;
 }
 
-static int ipxrtr_delete(long net)
+static ipx_interface *
+ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
 {
-	ipx_route **r = &ipx_router_list;
-	ipx_route *tmp;
+	struct datalink_proto *datalink = NULL;
+	ipx_interface	*intrfc;
+
+	switch (htons(dlink_type)) {
+	case ETH_P_IPX: datalink = pEII_datalink; break;
+	case ETH_P_802_2: datalink = p8022_datalink; break;
+	case ETH_P_SNAP: datalink = pSNAP_datalink; break;
+	case ETH_P_802_3: datalink = p8023_datalink; break;
+	default: return NULL;
+	}
+	
+	if (dev == NULL)
+		return NULL;
+
+	/* Check addresses are suitable */
+	if(dev->addr_len>IPX_NODE_LEN) return NULL;
+
+	if(dev->addr_len<2) return NULL;
+
+	intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+	if (intrfc!=NULL) {
+		intrfc->if_dev=dev;
+		intrfc->if_netnum=0L;
+		intrfc->if_dlink_type = dlink_type;
+		intrfc->if_dlink = datalink;
+		intrfc->if_sklist = NULL;
+		intrfc->if_internal = 0;
+		intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
+		intrfc->if_ipx_offset = dev->hard_header_len + 
+			datalink->header_length;
+		memset(intrfc->if_node, 0, IPX_NODE_LEN);
+		memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), 
+			dev->dev_addr, dev->addr_len);
+		ipxitf_insert(intrfc);
+	}
+
+	return intrfc;
+}
+
+static int 
+ipxitf_ioctl(unsigned int cmd, void *arg)
+{
+	int err;
+	switch(cmd)
+	{
+		case SIOCSIFADDR:
+		{
+			struct ifreq ifr;
+			struct sockaddr_ipx *sipx;
+			ipx_interface_definition f;
+			err=verify_area(VERIFY_READ,arg,sizeof(ifr));
+			if(err)
+				return err;
+			memcpy_fromfs(&ifr,arg,sizeof(ifr));
+			sipx=(struct sockaddr_ipx *)&ifr.ifr_addr;
+			if(sipx->sipx_family!=AF_IPX)
+				return -EINVAL;
+			f.ipx_network=sipx->sipx_network;
+			memcpy(f.ipx_device, ifr.ifr_name, sizeof(f.ipx_device));
+			f.ipx_dlink_type=sipx->sipx_type;
+			f.ipx_primary=sipx->sipx_primary;
+			f.ipx_internal=sipx->sipx_internal;
+			if(sipx->sipx_network==0)
+				return ipxitf_delete(&f);
+			else
+				return ipxitf_create(&f);
+		}
+		case SIOCGIFADDR:
+		{
+			struct ifreq ifr;
+			struct sockaddr_ipx *sipx;
+			ipx_interface *ipxif;
+			struct device *dev;
+			err=verify_area(VERIFY_WRITE,arg,sizeof(ifr));
+			if(err)
+				return err;
+			memcpy_fromfs(&ifr,arg,sizeof(ifr));
+			sipx=(struct sockaddr_ipx *)&ifr.ifr_addr;
+			dev=dev_get(ifr.ifr_name);
+			if(!dev)
+				return -ENODEV;
+			ipxif=ipxitf_find_using_phys(dev, sipx->sipx_type);
+			if(ipxif==NULL)
+				return -EADDRNOTAVAIL;
+			sipx->sipx_network=ipxif->if_netnum;
+			memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node));
+						
+		}
+		case SIOCAIPXITFCRT:
+			err=verify_area(VERIFY_READ,arg,sizeof(char));
+			if(err)
+				return err;
+			return ipxcfg_set_auto_create(get_fs_byte(arg));
+		case SIOCAIPXPRISLT:
+			err=verify_area(VERIFY_READ,arg,sizeof(char));
+			if(err)
+				return err;
+			return ipxcfg_set_auto_select(get_fs_byte(arg));
+		default:
+			return -EINVAL;
+	}
+}
+
+/*******************************************************************************************************************\
+*													            *
+*	            			Routing tables for the IPX socket layer				            *
+*														    *
+\*******************************************************************************************************************/
+
+static ipx_route *
+ipxrtr_lookup(unsigned long net)
+{
+	ipx_route *r;
+
+	for (r=ipx_routes; (r!=NULL) && (r->ir_net!=net); r=r->ir_next)
+		;
+
+	return r;
+}
 
-	while ((tmp = *r) != NULL) {
-		if (tmp->net == net) {
-			*r = tmp->next;
-			if (tmp->router_net == 0) {
-				ipxrtr_delete_localnet(tmp);
+static int
+ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
+{
+	ipx_route	*rt;
+
+	/* Get a route structure; either existing or create */
+	rt = ipxrtr_lookup(network);
+	if (rt==NULL) {
+		rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);
+		if(rt==NULL)
+			return -EAGAIN;
+		rt->ir_next=ipx_routes;
+		ipx_routes=rt;
+	}
+
+	rt->ir_net = network;
+	rt->ir_intrfc = intrfc;
+	if (node == NULL) {
+		memset(rt->ir_router_node, '\0', IPX_NODE_LEN);
+		rt->ir_routed = 0;
+	} else {
+		memcpy(rt->ir_router_node, node, IPX_NODE_LEN);
+		rt->ir_routed=1;
+	}
+	return 0;
+}
+
+static void
+ipxrtr_del_routes(ipx_interface *intrfc)
+{
+	ipx_route	**r, *tmp;
+
+	for (r = &ipx_routes; (tmp = *r) != NULL; ) {
+		if (tmp->ir_intrfc == intrfc) {
+			*r = tmp->ir_next;
+			kfree_s(tmp, sizeof(ipx_route));
+		} else {
+			r = &(tmp->ir_next);
+		}
+	}
+}
+
+static int 
+ipxrtr_create(ipx_route_definition *rd)
+{
+	ipx_interface *intrfc;
+
+	/* Find the appropriate interface */
+	intrfc = ipxitf_find_using_net(rd->ipx_router_network);
+	if (intrfc == NULL)
+		return -ENETUNREACH;
+
+	return ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node);
+}
+
+
+static int 
+ipxrtr_delete(long net)
+{
+	ipx_route	**r;
+	ipx_route	*tmp;
+
+	for (r = &ipx_routes; (tmp = *r) != NULL; ) {
+		if (tmp->ir_net == net) {
+			if (!(tmp->ir_routed)) {
+				/* Directly connected; can't lose route */
+				return -EPERM;
 			}
+			*r = tmp->ir_next;
 			kfree_s(tmp, sizeof(ipx_route));
 			return 0;
-		}
-		r = &tmp->next;
+		} 
+		r = &(tmp->ir_next);
 	}
+
 	return -ENOENT;
 }
 
-int ipxrtr_device_event(unsigned long event, void *ptr)
+static int
+ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, void *ubuf, int len)
+{
+	struct sk_buff *skb;
+	ipx_interface *intrfc;
+	ipx_packet *ipx;
+	int size;
+	int ipx_offset;
+	ipx_route *rt = NULL;
+
+	/* Find the appropriate interface on which to send packet */
+	if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) {
+		usipx->sipx_network = ipx_primary_net->if_netnum;
+		intrfc = ipx_primary_net;
+	} else {
+		rt = ipxrtr_lookup(usipx->sipx_network);
+		if (rt==NULL) {
+			return -ENETUNREACH;
+		}
+		intrfc = rt->ir_intrfc;
+	}
+	
+	ipx_offset = intrfc->if_ipx_offset;
+	size=sizeof(ipx_packet)+len;
+	size += ipx_offset;
+
+	if(size+sk->wmem_alloc>sk->sndbuf) return -EAGAIN;
+		
+	skb=alloc_skb(size,GFP_KERNEL);
+	if(skb==NULL) return -ENOMEM;
+		
+	skb->sk=sk;
+	skb->len=size;
+	skb->free=1;
+	skb->arp=1;
+
+	/* Fill in IPX header */
+	ipx=(ipx_packet *)&(skb->data[ipx_offset]);
+	ipx->ipx_checksum=0xFFFF;
+	ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
+	ipx->ipx_tctrl=0;
+	ipx->ipx_type=usipx->sipx_type;
+	skb->h.raw = (unsigned char *)ipx;
+
+	ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
+	memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+	ipx->ipx_source.sock = sk->ipx_port;
+	ipx->ipx_dest.net=usipx->sipx_network;
+	memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
+	ipx->ipx_dest.sock=usipx->sipx_port;
+
+	memcpy_fromfs((char *)(ipx+1),ubuf,len);
+	return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? 
+				rt->ir_router_node : ipx->ipx_dest.node);
+}
+	
+static int
+ipxrtr_route_skb(struct sk_buff *skb)
+{
+	ipx_packet	*ipx = (ipx_packet *) (skb->h.raw);
+	ipx_route	*r;
+	ipx_interface	*i;
+
+	r = ipxrtr_lookup(ipx->ipx_dest.net);
+	if (r == NULL) {
+		/* no known route */
+		kfree_skb(skb,FREE_READ);
+		return 0;
+	}
+	i = r->ir_intrfc;
+	(void)ipxitf_send(i, skb, (r->ir_routed) ? 
+			r->ir_router_node : ipx->ipx_dest.node);
+	return 0;
+}
+
+/*
+ *	We use a normal struct rtentry for route handling
+ */
+ 
+static int ipxrtr_ioctl(unsigned int cmd, void *arg)
+{
+	int err;
+	struct rtentry rt;	/* Use these to behave like 'other' stacks */
+	struct sockaddr_ipx *sg,*st;
+
+	err=verify_area(VERIFY_READ,arg,sizeof(rt));
+	if(err)
+		return err;
+		
+	memcpy_fromfs(&rt,arg,sizeof(rt));
+	
+	sg=(struct sockaddr_ipx *)&rt.rt_gateway;
+	st=(struct sockaddr_ipx *)&rt.rt_dst;
+	
+	if(!(rt.rt_flags&RTF_GATEWAY))
+		return -EINVAL;		/* Direct routes are fixed */
+	if(sg->sipx_family!=AF_IPX)
+		return -EINVAL;
+	if(st->sipx_family!=AF_IPX)
+		return -EINVAL;
+		
+	switch(cmd)
+	{
+		case SIOCDELRT:
+			return ipxrtr_delete(sg->sipx_network);
+		case SIOCADDRT:
+		{
+			struct ipx_route_definition f;
+			f.ipx_network=st->sipx_network;
+			f.ipx_router_network=sg->sipx_network;
+			memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN);
+			return ipxrtr_create(&f);
+		}
+		default:
+			return -EINVAL;
+	}
+}
+
+static char *
+ipx_frame_name(unsigned short frame)
+{
+	switch (ntohs(frame)) {
+	case ETH_P_IPX: return "EtherII";
+	case ETH_P_802_2: return "802.2";
+	case ETH_P_SNAP: return "SNAP";
+	case ETH_P_802_3: return "802.3";
+	default: return "None";
+	}
+}
+
+static char *
+ipx_device_name(ipx_interface *intrfc)
 {
-	struct device *dev=ptr;
-	ipx_route **r = &ipx_router_list;
-	ipx_route *tmp;
+	return (intrfc->if_internal ? "Internal" :
+		(intrfc->if_dev ? intrfc->if_dev->name : "Unknown"));
+}
 
-	if(event!=NETDEV_DOWN)
-		return NOTIFY_DONE;
-	while ((tmp = *r) != NULL) {
-		if (tmp->dev == dev) {
-			*r = tmp->next;
-			if(tmp->router_net == 0)
-				ipxrtr_delete_localnet(tmp);
-			kfree_s(tmp, sizeof(ipx_route));
+/* Called from proc fs */
+int 
+ipx_get_interface_info(char *buffer, char **start, off_t offset, int length)
+{
+	ipx_interface *i;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+
+	/* Theory.. Keep printing in the same place until we pass offset */
+	
+	len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network", 
+		"Node_Address", "Primary", "Device", "Frame_Type");
+	for (i = ipx_interfaces; i != NULL; i = i->if_next) {
+		len += sprintf(buffer+len, "%08lX   ", ntohl(i->if_netnum));
+		len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X   ", 
+				i->if_node[0], i->if_node[1], i->if_node[2],
+				i->if_node[3], i->if_node[4], i->if_node[5]);
+		len += sprintf(buffer+len, "%-9s", (i == ipx_primary_net) ?
+			"Yes" : "No");
+		len += sprintf (buffer+len, "%-11s", ipx_device_name(i));
+		len += sprintf (buffer+len, "%s\n", 
+			ipx_frame_name(i->if_dlink_type));
+
+		/* Are we still dumping unwanted data then discard the record */
+		pos=begin+len;
+		
+		if(pos<offset) {
+			len=0;			/* Keep dumping into the buffer start */
+			begin=pos;
 		}
-		r = &tmp->next;
+		if(pos>offset+length)		/* We have dumped enough */
+			break;
 	}
-	return NOTIFY_DONE;
+	
+	/* The data in question runs from begin to begin+len */
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Remove unwanted header data from length */
+	if(len>length)
+		len=length;		/* Remove unwanted tail data from length */
+	
+	return len;
 }
 
-static int ipxrtr_ioctl(unsigned int cmd, void *arg)
+int 
+ipx_get_info(char *buffer, char **start, off_t offset, int length)
 {
-	int err;
-	switch(cmd)
-	{
-		case SIOCDELRT:
-			err=verify_area(VERIFY_READ,arg,sizeof(long));
-			if(err)
-				return err;
-			return ipxrtr_delete(get_fs_long(arg));
-		case SIOCADDRT:
-		{
-			struct ipx_route_def f;
-			err=verify_area(VERIFY_READ,arg,sizeof(f));
-			if(err)
-				return err;
-			memcpy_fromfs(&f,arg,sizeof(f));
-			return ipxrtr_create(&f);
+	ipx_socket *s;
+	ipx_interface *i;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+
+	/* Theory.. Keep printing in the same place until we pass offset */
+	
+	len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address", 
+			"Remote_Address", "Tx_Queue", "Rx_Queue", 
+			"State", "Uid");
+	for (i = ipx_interfaces; i != NULL; i = i->if_next) {
+		for (s = i->if_sklist; s != NULL; s = s->next) {
+			len += sprintf (buffer+len,"%08lX:%04X  ", 
+				htonl(i->if_netnum),
+				htons(s->ipx_port));
+			if (s->state!=TCP_ESTABLISHED) {
+				len += sprintf(buffer+len, "%-28s", "Not_Connected");
+			} else {
+				len += sprintf (buffer+len,
+					"%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
+					htonl(s->ipx_dest_addr.net),
+					s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], 
+					s->ipx_dest_addr.node[2], s->ipx_dest_addr.node[3], 
+					s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
+					htons(s->ipx_dest_addr.sock));
+			}
+			len += sprintf (buffer+len,"%08lX  %08lX  ", 
+				s->wmem_alloc, s->rmem_alloc);
+			len += sprintf (buffer+len,"%02X     %03d\n", 
+				s->state, SOCK_INODE(s->socket)->i_uid);
+		
+			/* Are we still dumping unwanted data then discard the record */
+			pos=begin+len;
+		
+			if(pos<offset)
+			{
+				len=0;			/* Keep dumping into the buffer start */
+				begin=pos;
+			}
+			if(pos>offset+length)		/* We have dumped enough */
+				break;
 		}
-		default:
-			return -EINVAL;
 	}
+	
+	/* The data in question runs from begin to begin+len */
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Remove unwanted header data from length */
+	if(len>length)
+		len=length;		/* Remove unwanted tail data from length */
+	
+	return len;
 }
 
 int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length)
@@ -440,13 +1195,21 @@
 	off_t pos=0;
 	off_t begin=0;
 
-	len += sprintf (buffer,"Net      Router                Flags Dev   Frame\n");
-	for (rt = ipx_router_list; rt != NULL; rt = rt->next)
-	{
-		len += sprintf (buffer+len,"%08lX %08lX:%02X%02X%02X%02X%02X%02X %02X    %s  %d\n", ntohl(rt->net),
-			ntohl(rt->router_net), rt->router_node[0], rt->router_node[1], rt->router_node[2],
-			rt->router_node[3], rt->router_node[4], rt->router_node[5], rt->flags, rt->dev->name,
-			ntohs(rt->dlink_type));
+	len += sprintf (buffer,"%-11s%-13s%s\n", 
+			"Network", "Router_Net", "Router_Node");
+	for (rt = ipx_routes; rt != NULL; rt = rt->ir_next)
+	{
+		len += sprintf (buffer+len,"%08lX   ", ntohl(rt->ir_net));
+		if (rt->ir_routed) {
+			len += sprintf (buffer+len,"%08lX     %02X%02X%02X%02X%02X%02X\n", 
+				ntohl(rt->ir_intrfc->if_netnum), 
+				rt->ir_router_node[0], rt->ir_router_node[1], 
+				rt->ir_router_node[2], rt->ir_router_node[3], 
+				rt->ir_router_node[4], rt->ir_router_node[5]);
+		} else {
+			len += sprintf (buffer+len, "%-13s%s\n",
+					"Directly", "Connected");
+		}
 		pos=begin+len;
 		if(pos<offset)
 		{
@@ -471,7 +1234,6 @@
  
 static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	ipx_socket *sk=(ipx_socket *)sock->data;
 	switch(cmd)
 	{
 		default:
@@ -575,7 +1337,8 @@
 	}
 }
 
-static int ipx_create(struct socket *sock, int protocol)
+static int 
+ipx_create(struct socket *sock, int protocol)
 {
 	ipx_socket *sk;
 	sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
@@ -610,9 +1373,9 @@
 	sk->type=sock->type;
 	sk->ipx_type=0;		/* General user level IPX */
 	sk->debug=0;
-	
+	sk->ipx_intrfc = NULL;
 	memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
-	memset(&sk->ipx_source_addr,'\0',sizeof(sk->ipx_source_addr));
+	sk->ipx_port = 0;
 	sk->mtu=IPX_MTU;
 	
 	if(sock!=NULL)
@@ -627,12 +1390,7 @@
 	sk->error_report=def_callback1;
 
 	sk->zapped=1;
-	return(0);
-}
-
-static int ipx_dup(struct socket *newsock,struct socket *oldsock)
-{
-	return(ipx_create(newsock,SOCK_DGRAM));
+	return 0;
 }
 
 static int ipx_release(struct socket *sock, struct socket *peer)
@@ -647,87 +1405,73 @@
 	ipx_destroy_socket(sk);
 	return(0);
 }
-		
-static unsigned short first_free_socketnum(void)
+
+static int ipx_dup(struct socket *newsock,struct socket *oldsock)
+{
+	return(ipx_create(newsock,SOCK_DGRAM));
+}
+
+static unsigned short 
+ipx_first_free_socketnum(ipx_interface *intrfc)
 {
-	static unsigned short	socketNum = 0x4000;
+	unsigned short	socketNum = intrfc->if_sknum;
+
+	if (socketNum < IPX_MIN_EPHEMERAL_SOCKET)
+		socketNum = IPX_MIN_EPHEMERAL_SOCKET;
 
-	while (ipx_find_socket(ntohs(socketNum)) != NULL)
-		if (socketNum > 0x7ffc)
-			socketNum = 0x4000;
+	while (ipxitf_find_socket(intrfc, ntohs(socketNum)) != NULL)
+		if (socketNum > IPX_MAX_EPHEMERAL_SOCKET)
+			socketNum = IPX_MIN_EPHEMERAL_SOCKET;
 		else
 			socketNum++;
 
+	intrfc->if_sknum = socketNum;
 	return	ntohs(socketNum);
 }
 	
 static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
 {
 	ipx_socket *sk;
-	struct ipx_route *rt;
-	unsigned char	*nodestart;
+	ipx_interface *intrfc;
 	struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
 	
 	sk=(ipx_socket *)sock->data;
 	
 	if(sk->zapped==0)
-		return(-EIO);
+		return -EIO;
 		
 	if(addr_len!=sizeof(struct sockaddr_ipx))
 		return -EINVAL;
 	
-	if (addr->sipx_port == 0) 
-	{
-		addr->sipx_port = first_free_socketnum();
+	intrfc = ipxitf_find_using_net(addr->sipx_network);
+	if (intrfc == NULL)
+		return -EADDRNOTAVAIL;
+
+	if (addr->sipx_port == 0) {
+		addr->sipx_port = ipx_first_free_socketnum(intrfc);
 		if (addr->sipx_port == 0)
 			return -EINVAL;
 	}
-		
-	if(ntohs(addr->sipx_port)<0x4000 && !suser())
-		return(-EPERM);	/* protect IPX system stuff like routing/sap */
+
+	if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
+		return -EPERM;	/* protect IPX system stuff like routing/sap */
 	
 	/* Source addresses are easy. It must be our network:node pair for
 	   an interface routed to IPX with the ipx routing ioctl() */
 
-	if(ipx_find_socket(addr->sipx_port)!=NULL)
-	{
+	if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
 		if(sk->debug)
 			printk("IPX: bind failed because port %X in use.\n",
 				(int)addr->sipx_port);
 		return -EADDRINUSE;	   
 	}
 
-	sk->ipx_source_addr.sock=addr->sipx_port;
-
-	if (addr->sipx_network == 0L) 
-	{
-		rt = ipxrtr_get_default_net();
-	}
-	else 
-	{
-		rt = ipxrtr_get_dev(addr->sipx_network);
-	}
-
-	if(rt == NULL)
-	{
-		if(sk->debug)
-			printk("IPX: bind failed (no device for net %lX)\n",
-				sk->ipx_source_addr.net);
-		return -EADDRNOTAVAIL;
-	}
-
-	sk->ipx_source_addr.net=rt->net;
-
-	/* IPX addresses zero pad physical addresses less than 6 */
-	memset(sk->ipx_source_addr.node,'\0',6);
-	nodestart = sk->ipx_source_addr.node + (6 - rt->dev->addr_len);
-	memcpy(nodestart,rt->dev->dev_addr,rt->dev->addr_len);
-
-	ipx_insert_socket(sk);
+	sk->ipx_port=addr->sipx_port;
+	ipxitf_insert_socket(intrfc, sk);
 	sk->zapped=0;
 	if(sk->debug)
 		printk("IPX: socket is bound.\n");
-	return(0);
+	return 0;
 }
 
 static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
@@ -743,7 +1487,7 @@
 		return(-EINVAL);
 	addr=(struct sockaddr_ipx *)uaddr;
 	
-	if(sk->ipx_source_addr.net==0)
+	if(sk->ipx_port==0)
 	/* put the autobinding in */
 	{
 		struct sockaddr_ipx uaddr;
@@ -755,15 +1499,15 @@
 		if (ret != 0) return (ret);
 	}
 	
+	if(ipxrtr_lookup(addr->sipx_network)==NULL)
+		return -ENETUNREACH;
 	sk->ipx_dest_addr.net=addr->sipx_network;
 	sk->ipx_dest_addr.sock=addr->sipx_port;
-	memcpy(sk->ipx_dest_addr.node,addr->sipx_node,sizeof(sk->ipx_source_addr.node));
-	if(ipxrtr_get_dev(sk->ipx_dest_addr.net)==NULL)
-		return -ENETUNREACH;
+	memcpy(sk->ipx_dest_addr.node,addr->sipx_node,IPX_NODE_LEN);
 	sk->ipx_type=addr->sipx_type;
 	sock->state = SS_CONNECTED;
 	sk->state=TCP_ESTABLISHED;
-	return(0);
+	return 0;
 }
 
 static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
@@ -789,22 +1533,29 @@
 	
 	*uaddr_len = sizeof(struct sockaddr_ipx);
 		
-	if(peer)
-	{
+	if(peer) {
 		if(sk->state!=TCP_ESTABLISHED)
 			return -ENOTCONN;
 		addr=&sk->ipx_dest_addr;
+		sipx.sipx_network = addr->net;
+		memcpy(sipx.sipx_node,addr->node,IPX_NODE_LEN);
+		sipx.sipx_port = addr->sock;
+	} else {
+		if (sk->ipx_intrfc != NULL) {
+			sipx.sipx_network = sk->ipx_intrfc->if_netnum;
+			memcpy(sipx.sipx_node, sk->ipx_intrfc->if_node,
+				IPX_NODE_LEN);
+		} else {
+			sipx.sipx_network = 0L;
+			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
+		}
+		sipx.sipx_port = sk->ipx_port;
 	}
-	else
-		addr=&sk->ipx_source_addr;
 		
 	sipx.sipx_family = AF_IPX;
 	sipx.sipx_type = sk->ipx_type;
-	sipx.sipx_port = addr->sock;
-	sipx.sipx_network = addr->net;
-	memcpy(sipx.sipx_node,addr->node,sizeof(sipx.sipx_node));
 	memcpy(uaddr,&sipx,sizeof(sipx));
-	return(0);
+	return 0;
 }
 
 #if 0
@@ -854,171 +1605,40 @@
 int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	/* NULL here for pt means the packet was looped back */
-	ipx_socket *sock;
+	ipx_interface	*intrfc;
 	ipx_packet *ipx;
-	ipx_route *rt;
-	ipx_route *ln;
-	unsigned char IPXaddr[6];
 	
 	ipx=(ipx_packet *)skb->h.raw;
 	
-	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM)
-	{
+	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) {
 		/* We don't do checksum options. We can't really. Novell don't seem to have documented them.
 		   If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
 		   the same... */
 		kfree_skb(skb,FREE_READ);
-		return(0);
+		return 0;
 	}
 	
 	/* Too small */
-	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet))
-	{
-		kfree_skb(skb,FREE_READ);
-		return(0);
-	}
-	
-	/* Too many hops */
-	if(ipx->ipx_tctrl>16)
-	{
+	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)) {
 		kfree_skb(skb,FREE_READ);
-		return(0);
+		return 0;
 	}
 	
 	/* Determine what local ipx endpoint this is */
-	ln = ipxrtr_get_local_net(dev, pt->type);
-	if (ln == NULL) 
-	{
-		kfree_skb(skb,FREE_READ);
-		return(0);
-	}
-
-	memset(IPXaddr, '\0', 6);
-	memcpy(IPXaddr+(6 - dev->addr_len), dev->dev_addr, dev->addr_len);
-
-	/* Not us/broadcast */
-	if(memcmp(IPXaddr,ipx->ipx_dest.node,6)!=0
-	     && memcmp(ipx_broadcast_node,ipx->ipx_dest.node,6)!=0)
-	{
-		/**********************************************************************************************
-		
-			IPX router. Roughly as per the Novell spec. This doesn't handle netbios flood fill
-			broadcast frames. See the Novell IPX router specification for more details
-			(for ftp from ftp.novell.com)
-			
-		***********************************************************************************************/
-		
-		int incoming_size;
-		int outgoing_size;
-		struct sk_buff *skb2;
-		int free_it=0;
-		
-		/* Rule: Don't forward packets that have exceeded the hop limit. This is fixed at 16 in IPX */
-		if((ipx->ipx_tctrl==16) || (skb->pkt_type!=PACKET_HOST))
-		{
-			kfree_skb(skb,FREE_READ);
-			return(0);
+	intrfc = ipxitf_find_using_phys(dev, pt->type);
+	if (intrfc == NULL) {
+		if (ipxcfg_auto_create_interfaces) {
+			intrfc = ipxitf_auto_create(dev, pt->type);
 		}
 
-		ipx->ipx_tctrl++;
-		/* Don't forward if we don't have a route. We ought to go off and start hunting out routes but
-		   if someone needs this _THEY_ can add it */		
-		rt=ipxrtr_get_dev(ipx->ipx_dest.net);
-		if(rt==NULL)   /* Unlike IP we can send on the interface we received. Eg doing DIX/802.3 conversion */
-		{
+		if (intrfc == NULL) {
+			/* Not one of ours */
 			kfree_skb(skb,FREE_READ);
-			return(0);
-		}
-
-		/*
-		 *	Ok, before we forward, make sure this is not a local packet on the 
-		 *	other network 
-		 */
-		if (rt->router_net == 0) 
-		{
-			memset(IPXaddr,'\0',6);
-			memcpy(IPXaddr+(6-rt->dev->addr_len),rt->dev->dev_addr,rt->dev->addr_len);
-			if (memcmp(IPXaddr,ipx->ipx_dest.node,6) == 0) 
-				goto DELIVER;
-		}
-	
-		/* Check for differences in outgoing and incoming packet size */
-		incoming_size = skb->len - ntohs(ipx->ipx_pktsize);
-		outgoing_size = rt->datalink->header_length + rt->dev->hard_header_len;
-		if(incoming_size != outgoing_size)
-		{
-			/* A different header length causes a copy. Awkward to avoid with the current
-			   sk_buff stuff. */
-			skb2=alloc_skb(ntohs(ipx->ipx_pktsize) + outgoing_size,
-					GFP_ATOMIC);
-			if(skb2==NULL)
-			{
-				kfree_skb(skb,FREE_READ);
-				return 0;
-			}
-			free_it=1;
-			skb2->len=ntohs(ipx->ipx_pktsize) + outgoing_size;
-			skb2->mem_addr = skb2;
-			skb2->sk = NULL;
-			skb2->free = 1;
-			skb2->arp = 1;
-			skb2->len = ntohs(ipx->ipx_pktsize) + outgoing_size;
-
-			/*
-        		 *	NOTE: src arg for memcpy used to be (skb+1)+insize
-			 *	however, that doesn't work... (dunno why)
-			 *	it should though...
- 			 */
- 			
- 			memcpy((char *)(skb2+1)+outgoing_size,ipx,
-				ntohs(ipx->ipx_pktsize));
-		}
-		else
-		{
-			skb2=skb;
+			return 0;
 		}
-
-		/* Now operate on the buffer */
-		/* Increase hop count */
-		
-		skb2->dev = rt->dev;
-		rt->datalink->datalink_header(rt->datalink, skb2, 
-			(rt->flags&IPX_RT_ROUTED)?rt->router_node
-						:ipx->ipx_dest.node);
-
-		dev_queue_xmit(skb2,rt->dev,SOPRI_NORMAL);
-
-		if(free_it)
-			kfree_skb(skb,FREE_READ);
-		return(0);
-	}
-	/************ End of router: Now sanity check stuff for us ***************/
-DELIVER:	
-	/* Ok its for us ! */
-	if (ln->net == 0L) {
-/*		printk("IPX: Registering local net %lx\n", ipx->ipx_dest.net);*/
-		ln->net = ipx->ipx_dest.net;
-	}
-
-	sock=ipx_find_socket(ipx->ipx_dest.sock);
-	if(sock==NULL)	/* But not one of our sockets */
-	{
-		kfree_skb(skb,FREE_READ);
-		return(0);
 	}
 
-	/* Check to see if this socket needs its network number */
-	ln = ipxrtr_get_default_net();
-	if (sock->ipx_source_addr.net == 0L)
-		sock->ipx_source_addr.net = ln->net;
-	
-	if(sock_queue_rcv_skb(sock, skb)<0)
-	{
-		kfree_skb(skb,FREE_READ);	/* Socket is full */
-		return(0);
-	}
-	
-	return(0);
+	return ipxitf_rcv(intrfc, skb);
 }
 
 static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
@@ -1027,42 +1647,30 @@
 	ipx_socket *sk=(ipx_socket *)sock->data;
 	struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
 	struct sockaddr_ipx local_sipx;
-	struct sk_buff *skb;
-	struct device *dev;
-	struct ipx_packet *ipx;
-	int size;
-	ipx_route *rt;
-	struct datalink_proto *dl = NULL;
-	unsigned char IPXaddr[6];
-	int self_addressing = 0;
-	int broadcast = 0;
+	int retval;
 
-	if(flags)
-		return -EINVAL;
+	if (sk->zapped) return -EIO; /* Socket not bound */
+	if(flags) return -EINVAL;
 		
-	if(usipx)
-	{
-		if(sk->ipx_source_addr.net==0)
-		/* put the autobinding in */
-		{
+	if(usipx) {
+		if(sk->ipx_port == 0) {
 			struct sockaddr_ipx uaddr;
 			int ret;
 
 			uaddr.sipx_port = 0;
 			uaddr.sipx_network = 0L; 
 			ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
-			if (ret != 0) return (ret);
+			if (ret != 0) return ret;
 		}
 
 		if(addr_len <sizeof(*usipx))
-			return(-EINVAL);
+			return -EINVAL;
 		if(usipx->sipx_family != AF_IPX)
 			return -EINVAL;
-		if(htons(usipx->sipx_port)<0x4000 && !suser())
+
+		if(htons(usipx->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
 			return -EPERM;
-	}
-	else
-	{
+	} else {
 		if(sk->state!=TCP_ESTABLISHED)
 			return -ENOTCONN;
 		usipx=&local_sipx;
@@ -1070,130 +1678,12 @@
 		usipx->sipx_type=sk->ipx_type;
 		usipx->sipx_port=sk->ipx_dest_addr.sock;
 		usipx->sipx_network=sk->ipx_dest_addr.net;
-		memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,sizeof(usipx->sipx_node));
-	}
-	
-	if(sk->debug)
-		printk("IPX: sendto: Addresses built.\n");
-
-	if(memcmp(&usipx->sipx_node,&ipx_broadcast_node,6)==0) 
-	{
-		if (!sk->broadcast)
-			return -ENETUNREACH;
-		broadcast = 1;
-	}
-
-	/* Build a packet */
-	
-	if(sk->debug)
-		printk("IPX: sendto: building packet.\n");
-		
-	size=sizeof(ipx_packet)+len;	/* For mac headers */
-
-	/* Find out where this has to go */
-	if (usipx->sipx_network == 0L) {
-		rt = ipxrtr_get_default_net();
-		if (rt != NULL)
-			usipx->sipx_network = rt->net;
-	} else
-		rt=ipxrtr_get_dev(usipx->sipx_network);
-
-	if(rt==NULL)
-	{
-		return -ENETUNREACH;
-	}
-	
-	dev=rt->dev;
-	dl = rt->datalink;
-	
-	size += dev->hard_header_len;
-	size += dl->header_length;
-
-	if(sk->debug)
-		printk("IPX: sendto: allocating buffer (%d)\n",size);
-	
-	if(size+sk->wmem_alloc>sk->sndbuf) {
-		return -EAGAIN;
+		memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,IPX_NODE_LEN);
 	}
-		
-	skb=alloc_skb(size,GFP_KERNEL);
-	if(skb==NULL)
-		return -ENOMEM;
-		
-	skb->mem_addr=skb;
-	skb->sk=sk;
-	skb->free=1;
-	skb->arp=1;
-	skb->len=size;
-
-	sk->wmem_alloc+=skb->mem_len;
-
-	if(sk->debug)
-		printk("Building MAC header.\n");		
-	skb->dev=rt->dev;
 	
-	/* Build Data Link header */
-	dl->datalink_header(dl, skb, 
-		(rt->flags&IPX_RT_ROUTED)?rt->router_node:usipx->sipx_node);
-
-	/* See if we are sending to ourself */
-	memset(IPXaddr, '\0', 6);
-	memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr, 
-			skb->dev->addr_len);
-
-	self_addressing = !memcmp(IPXaddr, 
-				(rt->flags&IPX_RT_ROUTED)?rt->router_node
-				:usipx->sipx_node,
-				6);
-
-	/* Now the IPX */
-	if(sk->debug)
-		printk("Building IPX Header.\n");
-	ipx=(ipx_packet *)skb->h.raw;
-	ipx->ipx_checksum=0xFFFF;
-	ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
-	ipx->ipx_tctrl=0;
-	ipx->ipx_type=usipx->sipx_type;
-
-	memcpy(&ipx->ipx_source,&sk->ipx_source_addr,sizeof(ipx->ipx_source));
-	ipx->ipx_dest.net=usipx->sipx_network;
-	memcpy(ipx->ipx_dest.node,usipx->sipx_node,sizeof(ipx->ipx_dest.node));
-	ipx->ipx_dest.sock=usipx->sipx_port;
-	if(sk->debug)
-		printk("IPX: Appending user data.\n");
-	/* User data follows immediately after the IPX data */
-	memcpy_fromfs((char *)(ipx+1),ubuf,len);
-	if(sk->debug)
-		printk("IPX: Transmitting buffer\n");
-	if((dev->flags&IFF_LOOPBACK) || self_addressing) {
-		struct packet_type	pt;
-
-		/* loop back */
-		pt.type = rt->dlink_type;
-		sk->wmem_alloc-=skb->mem_len;
-		skb->sk = NULL;
-		ipx_rcv(skb,dev,&pt);
-	} else {
-		if (broadcast) {
-			struct packet_type	pt;
-			struct sk_buff		*skb2;
+	retval = ipxrtr_route_packet(sk, usipx, ubuf, len);
+	if (retval < 0) return retval;
 
-			/* loop back */
-			pt.type = rt->dlink_type;
-			
-			skb2=alloc_skb(skb->len, GFP_ATOMIC);
-			skb2->mem_addr=skb2;
-			skb2->free=1;
-			skb2->arp=1;
-			skb2->len=skb->len;
-			skb2->sk = NULL;
-			skb2->h.raw = skb2->data + rt->datalink->header_length
-				+ dev->hard_header_len;
-			memcpy(skb2->data, skb->data, skb->len);
-			ipx_rcv(skb2,dev,&pt);
-		}
-		dev_queue_xmit(skb,dev,SOPRI_NORMAL);
-	}
 	return len;
 }
 
@@ -1220,6 +1710,9 @@
 		return er;
 	}
 	
+	if (sk->zapped)
+		return -EIO;
+
 	if(addr_len)
 		*addr_len=sizeof(*sipx);
 
@@ -1235,7 +1728,7 @@
 	{
 		sipx->sipx_family=AF_IPX;
 		sipx->sipx_port=ipx->ipx_source.sock;
-		memcpy(sipx->sipx_node,ipx->ipx_source.node,sizeof(sipx->sipx_node));
+		memcpy(sipx->sipx_node,ipx->ipx_source.node,IPX_NODE_LEN);
 		sipx->sipx_network=ipx->ipx_source.net;
 		sipx->sipx_type = ipx->ipx_type;
 	}
@@ -1243,7 +1736,6 @@
 	return(copied);
 }		
 
-
 static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
 {
 	return ipx_send(sock,ubuf,size,noblock,0);
@@ -1309,6 +1801,20 @@
 			if(!suser())
 				return -EPERM;
 			return(ipxrtr_ioctl(cmd,(void *)arg));
+		case SIOCSIFADDR:
+		case SIOCGIFADDR:
+		case SIOCAIPXITFCRT:
+		case SIOCAIPXPRISLT:
+			if(!suser())
+				return -EPERM;
+			return(ipxitf_ioctl(cmd,(void *)arg));
+		case SIOCIPXCFGDATA: 
+		{
+			err=verify_area(VERIFY_WRITE,(void *)arg,
+				sizeof(ipx_config_data));
+			if(err) return err;
+			return(ipxcfg_get_config_data((void *)arg));
+		}
 		case SIOCGSTAMP:
 			if (sk)
 			{
@@ -1321,32 +1827,15 @@
 				return 0;
 			}
 			return -EINVAL;
-		case SIOCGIFCONF:
-		case SIOCGIFFLAGS:
-		case SIOCSIFFLAGS:
-		case SIOCGIFADDR:
-		case SIOCSIFADDR:
 		case SIOCGIFDSTADDR:
 		case SIOCSIFDSTADDR:
 		case SIOCGIFBRDADDR:
 		case SIOCSIFBRDADDR:
 		case SIOCGIFNETMASK:
 		case SIOCSIFNETMASK:
-		case SIOCGIFMETRIC:
-		case SIOCSIFMETRIC:
-		case SIOCGIFMEM:
-		case SIOCSIFMEM:
-		case SIOCGIFMTU:
-		case SIOCSIFMTU:
-		case SIOCSIFLINK:
-		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
-		case OLD_SIOCGIFHWADDR:
-			return(dev_ioctl(cmd,(void *) arg));
-
-
-		default:
 			return -EINVAL;
+		default:
+			return(dev_ioctl(cmd,(void *) arg));
 	}
 	/*NOTREACHED*/
 	return(0);
@@ -1384,7 +1873,7 @@
 
 {
 	0,	/* MUTTER ntohs(ETH_P_8023),*/
-	0,		/* copy */
+	NULL,		/* All devices */
 	ipx_rcv,
 	NULL,
 	NULL,
@@ -1393,14 +1882,14 @@
 static struct packet_type ipx_dix_packet_type = 
 {
 	0,	/* MUTTER ntohs(ETH_P_IPX),*/
-	NULL,		/* Al devices */
+	NULL,		/* All devices */
 	ipx_rcv,
 	NULL,
 	NULL,
 };
  
 static struct notifier_block ipx_dev_notifier={
-	ipxrtr_device_event,
+	ipxitf_device_event,
 	NULL,
 	0
 };
@@ -1412,6 +1901,8 @@
 void ipx_proto_init(struct net_proto *pro)
 {
 	unsigned char	val = 0xE0;
+	unsigned char	snapval[5] =  { 0x0, 0x0, 0x0, 0x81, 0x37 };
+
 	(void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
 
 	pEII_datalink = make_EII_client();
@@ -1425,9 +1916,12 @@
 	if ((p8022_datalink = register_8022_client(val, ipx_rcv)) == NULL)
 		printk("IPX: Unable to register with 802.2\n");
 
+	if ((pSNAP_datalink = register_snap_client(snapval, ipx_rcv)) == NULL)
+		printk("IPX: Unable to register with SNAP\n");
+	
 	register_netdevice_notifier(&ipx_dev_notifier);
 		
 	printk("Swansea University Computer Society IPX 0.29 BETA for NET3.019\n");
-	
+	printk("IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 #endif
diff -u --recursive --new-file v1.1.85/linux/net/inet/ipx.h linux/net/inet/ipx.h
--- v1.1.85/linux/net/inet/ipx.h	Fri May 27 10:46:44 1994
+++ linux/net/inet/ipx.h	Tue Jan 24 15:27:55 1995
@@ -11,13 +11,14 @@
 #ifndef _NET_INET_IPX_H_
 #define _NET_INET_IPX_H_
 
-#include <linux/ipx.h>
+#include <linux/skbuff.h>
 #include "datalink.h"
+#include <linux/ipx.h>
 
 typedef struct
 {
 	unsigned long net;
-	unsigned char  node[6]; 
+	unsigned char  node[IPX_NODE_LEN]; 
 	unsigned short sock;
 } ipx_address;
 
@@ -41,31 +42,43 @@
 } ipx_packet;
 
 
-typedef struct ipx_route
-{
-	unsigned long net;
-	unsigned char router_node[6];
-	unsigned long router_net;
-	unsigned short flags;
-#define IPX_RT_ROUTED	1		/* This isn't a direct route. Send via this if to node router_node */
-#define IPX_RT_BLUEBOOK	2		
-#define IPX_RT_8022	4		
-#define IPX_RT_SNAP	8		
-	unsigned short dlink_type;
-	struct device *dev;
-	struct datalink_proto *datalink;
-	struct ipx_route *next;
-	struct ipx_route *nextlocal;
-} ipx_route;
-
-
 typedef struct sock ipx_socket;
 
-
 #include "ipxcall.h"
 extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
 extern void ipxrtr_device_down(struct device *dev);
 
+typedef struct ipx_interface {
+	/* IPX address */
+	unsigned long	if_netnum;
+	unsigned char	if_node[IPX_NODE_LEN];
+
+	/* physical device info */
+	struct device	*if_dev;
+	struct datalink_proto	*if_dlink;
+	unsigned short	if_dlink_type;
+
+	/* socket support */
+	unsigned short	if_sknum;
+	ipx_socket	*if_sklist;
+
+	/* administrative overhead */
+	int		if_ipx_offset;
+	unsigned char	if_internal;
+	unsigned char	if_primary;
+	
+	struct ipx_interface	*if_next;
+}	ipx_interface;
+
+typedef struct ipx_route {
+	unsigned long ir_net;
+	ipx_interface *ir_intrfc;
+	unsigned char ir_routed;
+	unsigned char ir_router_node[IPX_NODE_LEN];
+	struct ipx_route *ir_next;
+}	ipx_route;
 
+#define IPX_MIN_EPHEMERAL_SOCKET	0x4000
+#define IPX_MAX_EPHEMERAL_SOCKET	0x7fff
 
 #endif
diff -u --recursive --new-file v1.1.85/linux/net/inet/p8022.c linux/net/inet/p8022.c
--- v1.1.85/linux/net/inet/p8022.c	Mon Jan  9 07:22:13 1995
+++ linux/net/inet/p8022.c	Tue Jan 24 15:27:55 1995
@@ -27,6 +27,7 @@
 	proto = find_8022_client(*(skb->h.raw));
 	if (proto != NULL) {
 		skb->h.raw += 3;
+		skb->len -= 3;
 		return proto->rcvfunc(skb, dev, pt);
 	}
 
@@ -87,7 +88,7 @@
 		proto->rcvfunc = rcvfunc;
 		proto->header_length = 3;
 		proto->datalink_header = p8022_datalink_header;
-
+		proto->string_name = "802.2";
 		proto->next = p8022_list;
 		p8022_list = proto;
 	}
diff -u --recursive --new-file v1.1.85/linux/net/inet/p8023.c linux/net/inet/p8023.c
--- v1.1.85/linux/net/inet/p8023.c	Mon May 23 08:17:49 1994
+++ linux/net/inet/p8023.c	Tue Jan 24 15:27:55 1995
@@ -27,6 +27,7 @@
 		proto->type_len = 0;
 		proto->header_length = 0;
 		proto->datalink_header = p8023_datalink_header;
+		proto->string_name = "802.3";
 	}
 
 	return proto;
diff -u --recursive --new-file v1.1.85/linux/net/inet/pe2.c linux/net/inet/pe2.c
--- v1.1.85/linux/net/inet/pe2.c	Mon May 23 08:17:50 1994
+++ linux/net/inet/pe2.c	Tue Jan 24 15:27:55 1995
@@ -27,6 +27,7 @@
 		proto->type_len = 0;
 		proto->header_length = 0;
 		proto->datalink_header = pEII_datalink_header;
+		proto->string_name = "EtherII";
 	}
 
 	return proto;
diff -u --recursive --new-file v1.1.85/linux/net/inet/protocol.c linux/net/inet/protocol.c
--- v1.1.85/linux/net/inet/protocol.c	Mon Jan  9 07:22:13 1995
+++ linux/net/inet/protocol.c	Tue Jan 24 15:31:48 1995
@@ -40,7 +40,7 @@
 #include "sock.h"
 #include "icmp.h"
 #include "udp.h"
-#include "igmp.h"
+#include <linux/igmp.h>
 
 
 static struct inet_protocol tcp_protocol = {
diff -u --recursive --new-file v1.1.85/linux/net/inet/psnap.c linux/net/inet/psnap.c
--- v1.1.85/linux/net/inet/psnap.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/psnap.c	Tue Jan 24 15:27:56 1995
@@ -0,0 +1,111 @@
+/*
+ *	SNAP data link layer. Derived from 802.2
+ *
+ *		Alan Cox <Alan.Cox@linux.org>, from the 802.2 layer by Greg Page.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+ 
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include "p8022.h"
+#include "psnap.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static struct datalink_proto *snap_list = NULL;
+static struct datalink_proto *snap_dl = NULL;		/* 802.2 DL for SNAP */
+
+/*
+ *	Find a snap client by matching the 5 bytes.
+ */
+ 
+static struct datalink_proto *find_snap_client(unsigned char *desc)
+{
+	struct datalink_proto	*proto;
+
+	for (proto = snap_list; proto != NULL && memcmp(proto->type, desc, 5) ; proto = proto->next);
+	return proto;
+}
+
+/*
+ *	A SNAP packet has arrived
+ */
+ 
+int snap_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct datalink_proto	*proto;
+
+	proto = find_snap_client(skb->h.raw);
+	if (proto != NULL) 
+	{
+		/*
+		 *	Pass the frame on.
+		 */
+		 
+		skb->h.raw += 5;
+		skb->len -= 5;
+		return proto->rcvfunc(skb, dev, pt);
+	}
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+/*
+ *	Put a SNAP header on a frame and pass to 802.2
+ */
+ 
+static void snap_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node)
+{
+	struct device	*dev = skb->dev;
+	unsigned char	*rawp;
+
+	rawp = skb->data + snap_dl->header_length+dev->hard_header_len;
+	memcpy(rawp,dl->type,5);
+	skb->h.raw = rawp+5;
+	snap_dl->datalink_header(snap_dl, skb, dest_node);
+}
+
+/*
+ *	Set up the SNAP layer
+ */
+ 
+void snap_proto_init(struct net_proto *pro)
+{
+	snap_dl=register_8022_client(0xAA, snap_rcv);
+	if(snap_dl==NULL)
+		printk("SNAP - unable to register with 802.2\n");
+}
+	
+/*
+ *	Register SNAP clients. We don't yet use this for IP or IPX.
+ */
+ 
+struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+{
+	struct datalink_proto	*proto;
+
+	if (find_snap_client(desc) != NULL)
+		return NULL;
+
+	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+	if (proto != NULL) 
+	{
+		memcpy(proto->type, desc,5);
+		proto->type_len = 5;
+		proto->rcvfunc = rcvfunc;
+		proto->header_length = 5+snap_dl->header_length;
+		proto->datalink_header = snap_datalink_header;
+		proto->string_name = "SNAP";
+		proto->next = snap_list;
+		snap_list = proto;
+	}
+
+	return proto;
+}
+
diff -u --recursive --new-file v1.1.85/linux/net/inet/psnap.h linux/net/inet/psnap.h
--- v1.1.85/linux/net/inet/psnap.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/psnap.h	Tue Jan 24 15:27:56 1995
@@ -0,0 +1,2 @@
+struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff -u --recursive --new-file v1.1.85/linux/net/inet/psnapcall.h linux/net/inet/psnapcall.h
--- v1.1.85/linux/net/inet/psnapcall.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/psnapcall.h	Tue Jan 24 15:27:56 1995
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void snap_proto_init(struct net_proto *);
diff -u --recursive --new-file v1.1.85/linux/net/inet/sock.h linux/net/inet/sock.h
--- v1.1.85/linux/net/inet/sock.h	Fri Jan 13 10:12:24 1995
+++ linux/net/inet/sock.h	Tue Jan 24 15:31:48 1995
@@ -43,8 +43,11 @@
 #ifdef CONFIG_IPX
 #include "ipx.h"
 #endif
+#ifdef CONFIG_ATALK
+#include <linux/atalk.h>
+#endif
 
-#include "igmp.h"
+#include <linux/igmp.h>
 
 #define SOCK_ARRAY_SIZE	256		/* Think big (also on some systems a byte is faster */
 
@@ -139,7 +142,9 @@
   unsigned short		type;
   unsigned char			localroute;	/* Route locally only */
 #ifdef CONFIG_IPX
-  ipx_address			ipx_source_addr,ipx_dest_addr;
+  ipx_address			ipx_dest_addr;
+  ipx_interface			*ipx_intrfc;
+  unsigned short		ipx_port;
   unsigned short		ipx_type;
 #endif
 #ifdef CONFIG_AX25
@@ -157,6 +162,10 @@
   unsigned short		ax25_t1,ax25_t2,ax25_t3;
   ax25_digi			*ax25_digipeat;
 #endif  
+#ifdef CONFIG_ATALK
+  struct atalk_sock		at;
+#endif
+
 /* IP 'private area' or will be eventually */
   int				ip_ttl;		/* TTL setting */
   int				ip_tos;		/* TOS */
diff -u --recursive --new-file v1.1.85/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.85/linux/net/inet/tcp.c	Sun Jan 22 21:39:45 1995
+++ linux/net/inet/tcp.c	Fri Jan 27 11:05:21 1995
@@ -131,6 +131,7 @@
  *		Alan Cox	:	Extracted closing code better
  *		Alan Cox	:	Fixed the closing state machine to
  *					resemble the RFC.
+ *		Alan Cox	:	More 'per spec' fixes.
  *
  *
  * To Fix:
@@ -202,6 +203,7 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/time.h>
 #include <linux/string.h>
 #include <linux/config.h>
 #include <linux/socket.h>
@@ -365,12 +367,12 @@
  *	tcp_close, and timeout mirrors the value there. 
  */
 
-static void tcp_close_pending (struct sock *sk, int timeout) 
+static void tcp_close_pending (struct sock *sk) 
 {
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
-		tcp_close(skb->sk, timeout);
+		tcp_close(skb->sk, 0);
 		kfree_skb(skb, FREE_READ);
 	}
 	return;
@@ -399,6 +401,7 @@
 	struct sk_buff * skb;
 	struct proto *prot;
 	struct device *dev;
+	int ct=0;
 
 	prot = sk->prot;
 	skb = sk->send_head;
@@ -480,7 +483,7 @@
 		 *	Count retransmissions
 		 */
 		 
-		sk->retransmits++;
+		ct++;
 		sk->prot->retransmits ++;
 
 		/*
@@ -494,7 +497,7 @@
 		 *	This should cut it off before we send too many packets.
 		 */
 
-		if (sk->retransmits >= sk->cong_window)
+		if (ct >= sk->cong_window)
 			break;
 		skb = skb->link3;
 	}
@@ -600,7 +603,7 @@
 	/*
 	 *	Has it gone just too far ?
 	 */
-	if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
+	if (sk->retransmits > TCP_RETR2) 
 	{
 		sk->err = ETIMEDOUT;
 		/*
@@ -616,7 +619,7 @@
 			/*
 			 *	Clean up time.
 			 */
-			sk->prot->close (sk, 1);
+			tcp_set_state(sk, TCP_CLOSE);
 			return 0;
 		}
 	}
@@ -2241,7 +2244,7 @@
  *	closed.
  */
  
-static int tcp_close_state(struct sock *sk)
+static int tcp_close_state(struct sock *sk, int dead)
 {
 	int ns=TCP_CLOSE;
 	int send_fin=0;
@@ -2279,7 +2282,7 @@
 	 *	that we won't make the old 4*rto = almost no time - whoops
 	 *	reset mistake.
 	 */
-	if(sk->dead && ns==TCP_FIN_WAIT2)
+	if(dead && ns==TCP_FIN_WAIT2)
 	{
 		int timer_active=del_timer(&sk->timer);
 		if(timer_active)
@@ -2334,6 +2337,7 @@
 			   sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
 	if (tmp < 0) 
 	{
+		int t;
   		/*
   		 *	Finish anyway, treat this as a send that got lost. 
   		 *	(Not good).
@@ -2342,6 +2346,11 @@
 	  	buff->free = 1;
 		prot->wfree(sk,buff->mem_addr, buff->mem_len);
 		sk->write_seq++;
+		t=del_timer(&sk->timer);
+		if(t)
+			add_timer(&sk->timer);
+		else
+			reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
 		return;
 	}
 	
@@ -2438,7 +2447,7 @@
 	 *	FIN if needed
 	 */
 	 
-	if(tcp_close_state(sk))
+	if(tcp_close_state(sk,0))
 		tcp_send_fin(sk);
 		
 	release_sock(sk);
@@ -2936,7 +2945,7 @@
 	{
 		/* Special case */
 		tcp_set_state(sk, TCP_CLOSE);
-		tcp_close_pending(sk, timeout);
+		tcp_close_pending(sk);
 		release_sock(sk);
 		return;
 	}
@@ -2959,14 +2968,14 @@
 		 
 		while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
 			kfree_skb(skb, FREE_READ);
+		/*
+		 *	Get rid off any half-completed packets. 
+		 */
+
+		if (sk->partial) 
+			tcp_send_partial(sk);
 	}
 
-	/*
-	 *	Get rid off any half-completed packets. 
-	 */
-	 
-	if (sk->partial) 
-		tcp_send_partial(sk);
 		
 	/*
 	 *	Timeout is not the same thing - however the code likes
@@ -2975,21 +2984,11 @@
 	 
 	if(timeout)
 	{
-		/*
-		 *	Time wait to avoid port reusage accidents if 
-		 *	appropriate. If we have timed out from one
-		 *	of these states then move straight to close.
-		 */
-		 
-		if( sk->state == TCP_TIME_WAIT || sk->state == TCP_LAST_ACK 
-			|| sk->state == TCP_SYN_SENT || sk->state == TCP_CLOSE)
-			tcp_set_state(sk, TCP_CLOSE);	/* Dead */
-		else
-			tcp_time_wait(sk);			
+		tcp_set_state(sk, TCP_CLOSE);	/* Dead */
 	}
 	else
 	{
-		if(tcp_close_state(sk)==1)
+		if(tcp_close_state(sk,1)==1)
 		{
 			tcp_send_fin(sk);
 		}
@@ -3570,7 +3569,7 @@
 		if(sk->debug)
 			printk("rcv_ack_seq: %lX==%lX, acked_seq: %lX==%lX\n",
 				sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq);
-		if (sk->rcv_ack_seq == sk->write_seq && sk->acked_seq == sk->fin_seq) 
+		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 
 		{
 			flag |= 1;
 			tcp_set_state(sk,TCP_CLOSE);
@@ -3715,8 +3714,7 @@
 			/*
 			 * move to CLOSE_WAIT, tcp_data() already handled
 			 * sending the ack.
-			 */	/* Check me --------------vvvvvvv */
-			reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			 */
 			tcp_set_state(sk,TCP_CLOSE_WAIT);
 			if (th->rst)
 				sk->shutdown = SHUTDOWN_MASK;
diff -u --recursive --new-file v1.1.85/linux/net/inet/udp.c linux/net/inet/udp.c
--- v1.1.85/linux/net/inet/udp.c	Wed Jan 11 21:14:30 1995
+++ linux/net/inet/udp.c	Thu Jan 26 07:25:54 1995
@@ -650,7 +650,7 @@
   		udp_statistics.UdpNoPorts++;
 		if (addr_type == IS_MYADDR) 
 		{
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
 		}
 		/*
 		 * Hmm.  We got an UDP broadcast to a port to which we
diff -u --recursive --new-file v1.1.85/linux/net/protocols.c linux/net/protocols.c
--- v1.1.85/linux/net/protocols.c	Tue Aug  9 09:35:23 1994
+++ linux/net/protocols.c	Tue Jan 24 15:27:56 1995
@@ -25,6 +25,13 @@
 #ifdef CONFIG_AX25
 #include "inet/ax25call.h"
 #endif
+#ifdef CONFIG_ATALK
+#ifndef CONFIG_IPX
+#include "inet/p8022call.h"
+#endif
+#include "inet/atalkcall.h"
+#endif
+#include "inet/psnapcall.h"
 
 /*
  *	Protocol Table
@@ -34,15 +41,21 @@
 #ifdef	CONFIG_UNIX
   { "UNIX",	unix_proto_init	},
 #endif
-#ifdef  CONFIG_IPX
-  { "IPX",	ipx_proto_init },
+#if defined(CONFIG_IPX)||defined(CONFIG_ATALK)  
   { "802.2",	p8022_proto_init },
+  { "SNAP",	snap_proto_init },
 #endif
 #ifdef CONFIG_AX25  
   { "AX.25",	ax25_proto_init },
 #endif  
 #ifdef	CONFIG_INET
   { "INET",	inet_proto_init	},
+#endif
+#ifdef  CONFIG_IPX
+  { "IPX",	ipx_proto_init },
+#endif
+#ifdef CONFIG_ATALK
+  { "DDP",	atalk_proto_init },
 #endif
   { NULL,	NULL		}
 };
