## Automatically generated incremental diff
## From:   linux-2.0.31-pre3
## To:     linux-2.0.31-pre4
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.0.31-pre3/arch/i386/kernel/entry.S linux-2.0.31-pre4/arch/i386/kernel/entry.S
--- linux-2.0.31-pre3/arch/i386/kernel/entry.S	1996-12-11 06:41:01.000000000 -0800
+++ linux-2.0.31-pre4/arch/i386/kernel/entry.S	2003-08-15 15:03:56.000000000 -0700
@@ -132,7 +132,10 @@
 	decl SYMBOL_NAME(syscall_count); \
 	decl SYMBOL_NAME(kernel_counter); \
 	jnz 1f;	\
-	movb $(NO_PROC_ID), SYMBOL_NAME(active_kernel_processor); \
+	movb SYMBOL_NAME(saved_active_kernel_processor), %al; \
+	movb %al, SYMBOL_NAME(active_kernel_processor); \
+	cmpb $(NO_PROC_ID), %al; \
+	jnz 1f; \
 	lock; \
 	btrl $0, SYMBOL_NAME(kernel_flag); \
 1:	popfl;
@@ -163,9 +166,13 @@
 
 #define	ENTER_KERNEL \
 	pushl %eax; \
+	pushl %ebx; \
+	pushl %ecx; \
 	pushl %edx; \
 	pushfl; \
 	cli; \
+	movl $6000, %ebx; \
+	movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
 	GET_PROCESSOR_ID \
 	btsl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \
 	SMP_PROF_A \
@@ -182,7 +189,18 @@
 	jnc 5f; \
 	movl %cr3,%edx; \
 	movl %edx,%cr3; \
-5:	btl $0, SYMBOL_NAME(kernel_flag); \
+5:	sti; \
+	decl %ecx; \
+	cli; \
+	jne 7f; \
+	decl %ebx; \
+	jne 6f; \
+	call SYMBOL_NAME(non_irq_deadlock_detected); \
+6:	movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
+	cmpb SYMBOL_NAME(boot_cpu_id), %al; \
+	jne 7f; \
+	incl SYMBOL_NAME(jiffies);  \
+7:	btl $0, SYMBOL_NAME(kernel_flag); \
 	jc 2b; \
 	jmp 1b; \
 3:	movb %al, SYMBOL_NAME(active_kernel_processor); \
@@ -190,6 +208,8 @@
 	incl SYMBOL_NAME(syscall_count); \
 	popfl; \
 	popl %edx; \
+	popl %ecx; \
+	popl %ebx; \
 	popl %eax;
 
 
@@ -325,6 +345,10 @@
 9:	movl SYMBOL_NAME(bh_mask),%eax
 	andl SYMBOL_NAME(bh_active),%eax
 	jne handle_bottom_half
+#ifdef __SMP__
+	cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor)
+	jne 2f
+#endif
 	movl EFLAGS(%esp),%eax		# check VM86 flag: CS/SS are
 	testl $(VM_MASK),%eax		# different then
 	jne 1f
diff -urN linux-2.0.31-pre3/arch/i386/kernel/smp.c linux-2.0.31-pre4/arch/i386/kernel/smp.c
--- linux-2.0.31-pre3/arch/i386/kernel/smp.c	1996-05-12 21:17:23.000000000 -0700
+++ linux-2.0.31-pre4/arch/i386/kernel/smp.c	2003-08-15 15:03:56.000000000 -0700
@@ -1202,3 +1202,15 @@
 	apic_read(APIC_SPIV);		/* Dummy read */
 	apic_write(APIC_EOI, 0);	/* Docs say use 0 for future compatibility */
 }
+
+void irq_deadlock_detected(void)
+{
+  printk("IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
+  __asm__("hlt");
+}
+
+void non_irq_deadlock_detected(void)
+{
+  printk("NON-IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
+  __asm__("hlt");
+}
diff -urN linux-2.0.31-pre3/arch/i386/kernel/traps.c linux-2.0.31-pre4/arch/i386/kernel/traps.c
--- linux-2.0.31-pre3/arch/i386/kernel/traps.c	1996-12-11 06:41:01.000000000 -0800
+++ linux-2.0.31-pre4/arch/i386/kernel/traps.c	2003-08-15 15:03:56.000000000 -0700
@@ -276,7 +276,10 @@
 asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
 					  long error_code)
 {
+#if 0
+	/* No need to warn about this any longer. */
 	printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
+#endif
 }
 
 /*
diff -urN linux-2.0.31-pre3/Documentation/Configure.help linux-2.0.31-pre4/Documentation/Configure.help
--- linux-2.0.31-pre3/Documentation/Configure.help	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/Documentation/Configure.help	2003-08-15 15:03:56.000000000 -0700
@@ -971,12 +971,14 @@
   If you want this, say Y.
 
 IP: ipautofw masquerade support
-CONFIG_IP_MASQUERADE_IPAUTOFW
+CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental)
   ipautofw is a program by Richard Lynch allowing additional
   support for masquerading protocols which do not (as yet)
   have additional protocol helpers.  
   Information and source for ipautofw is available from
   ftp://ftp.netis.com/pub/members/rlynch/
+  The ipautofw code is still under development and so is currently
+  marked EXPERIMENTAL.
   If you want this, say Y.
 
 IP: ICMP masquerading
@@ -2129,6 +2131,20 @@
   mixing an SMC Ultra and an Adaptec AHA1542 SCSI card causes corruption
   problems with many operating systems.
 
+SMC Ultra32 support
+CONFIG_ULTRA32
+  This is support for the SMC Ultra32 EISA card in shared memory mode.
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile
+  it as a module, say M here and read Documentation/modules.txt as
+  well as Documentation/networking/net-modules.txt. If you plan to use
+  more than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
 SMC 9194 Support
 CONFIG_SMC9194
   This is support for the SMC9xxx based Ethernet cards.  Choose this
diff -urN linux-2.0.31-pre3/drivers/char/keyboard.c linux-2.0.31-pre4/drivers/char/keyboard.c
--- linux-2.0.31-pre3/drivers/char/keyboard.c	1997-03-11 15:55:22.000000000 -0800
+++ linux-2.0.31-pre4/drivers/char/keyboard.c	2003-08-15 15:03:56.000000000 -0700
@@ -1078,6 +1078,8 @@
 		reply_expected = 1;
 		outb_p(data, 0x60);
 		for(i=0; i<0x200000; i++) {
+			extern void allow_interrupts(void);
+			allow_interrupts();
 			inb_p(0x64);		/* just as a delay */
 			if (acknowledge)
 				return 1;
diff -urN linux-2.0.31-pre3/drivers/char/n_tty.c linux-2.0.31-pre4/drivers/char/n_tty.c
--- linux-2.0.31-pre3/drivers/char/n_tty.c	1996-09-02 05:18:26.000000000 -0700
+++ linux-2.0.31-pre4/drivers/char/n_tty.c	2003-08-15 15:03:56.000000000 -0700
@@ -89,6 +89,13 @@
  */
 int n_tty_chars_in_buffer(struct tty_struct *tty)
 {
+	if (tty->icanon) {
+		if (!tty->canon_data) return 0;
+
+		return (tty->canon_head > tty->read_tail) ?
+			tty->canon_head - tty->read_tail :
+			tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
+	}
 	return tty->read_cnt;
 }
 
diff -urN linux-2.0.31-pre3/drivers/char/pty.c linux-2.0.31-pre4/drivers/char/pty.c
--- linux-2.0.31-pre3/drivers/char/pty.c	1996-09-30 06:56:48.000000000 -0700
+++ linux-2.0.31-pre4/drivers/char/pty.c	2003-08-15 15:03:56.000000000 -0700
@@ -153,14 +153,32 @@
 	return to->ldisc.receive_room(to);
 }
 
+/*
+ * Modified for asymmetric master/slave behavior
+ * The chars_in_buffer() value is used by the ldisc select() function 
+ * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
+ * To allow typed-ahead commands to accumulate, the master side returns 0
+ * until the buffer is half full. The slave side returns the true count.
+ */
 static int pty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct tty_struct *to = tty->link;
+	int count;
 
 	if (!to || !to->ldisc.chars_in_buffer)
 		return 0;
 
-	return to->ldisc.chars_in_buffer(to);
+	/* The ldisc must report 0 if no characters available to be read */
+	count = to->ldisc.chars_in_buffer(to);
+
+	if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
+
+	/* 
+	 * Master side driver ... return 0 if the other side's read buffer 
+	 * is less than half full.  This allows room for typed-ahead commands
+	 * with a reasonable margin to avoid overflow. 
+	 */
+	return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
 }
 
 static void pty_flush_buffer(struct tty_struct *tty)
diff -urN linux-2.0.31-pre3/drivers/char/random.c linux-2.0.31-pre4/drivers/char/random.c
--- linux-2.0.31-pre3/drivers/char/random.c	1997-04-08 08:47:45.000000000 -0700
+++ linux-2.0.31-pre4/drivers/char/random.c	2003-08-15 15:03:56.000000000 -0700
@@ -1071,10 +1071,8 @@
 	 * If we gave the user some bytes and we have an inode pointer,
 	 * update the access time.
 	 */
-	if (inode && count != 0) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	if (inode && count != 0)
+		UPDATE_ATIME(inode);
 	
 	return (count ? count : retval);
 }
diff -urN linux-2.0.31-pre3/drivers/isdn/hisax/avm_a1.c linux-2.0.31-pre4/drivers/isdn/hisax/avm_a1.c
--- linux-2.0.31-pre3/drivers/isdn/hisax/avm_a1.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/hisax/avm_a1.c	2003-08-15 15:03:56.000000000 -0700
@@ -226,7 +226,7 @@
 
 		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
+			if (!(r & 0x80))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX invalid frame");
 			if ((r & 0x40) && hsp->mode)
@@ -235,7 +235,7 @@
 						hsp->mode);
 					debugl1(sp, tmp);
 				}
-			if (!r & 0x20)
+			if (!(r & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX CRC error");
 			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
@@ -402,7 +402,7 @@
 			if (exval & 0x40)
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
+			if (!(exval & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC CRC error");
 			writereg(sp->isac, ISAC_CMDR, 0x80);
diff -urN linux-2.0.31-pre3/drivers/isdn/hisax/elsa.c linux-2.0.31-pre4/drivers/isdn/hisax/elsa.c
--- linux-2.0.31-pre3/drivers/isdn/hisax/elsa.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/hisax/elsa.c	2003-08-15 15:03:56.000000000 -0700
@@ -433,7 +433,7 @@
 
 		r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
+			if (!(r & 0x80))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX invalid frame");
 			if ((r & 0x40) && hsp->mode)
@@ -442,7 +442,7 @@
 						hsp->mode);
 					debugl1(sp, tmp);
 				}
-			if (!r & 0x20)
+			if (!(r & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX CRC error");
 			writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
@@ -615,7 +615,7 @@
 			if (exval & 0x40)
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
+			if (!(exval & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC CRC error");
 			writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
diff -urN linux-2.0.31-pre3/drivers/isdn/hisax/ix1_micro.c linux-2.0.31-pre4/drivers/isdn/hisax/ix1_micro.c
--- linux-2.0.31-pre3/drivers/isdn/hisax/ix1_micro.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/hisax/ix1_micro.c	2003-08-15 15:03:56.000000000 -0700
@@ -308,7 +308,7 @@
 
 		r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
+			if (!(r & 0x80))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX invalid frame");
 			if ((r & 0x40) && hsp->mode)
@@ -317,7 +317,7 @@
 						hsp->mode);
 					debugl1(sp, tmp);
 				}
-			if (!r & 0x20)
+			if (!(r & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX CRC error");
 			writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
@@ -488,7 +488,7 @@
 			if (exval & 0x40)
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
+			if (!(exval & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC CRC error");
 			IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
diff -urN linux-2.0.31-pre3/drivers/isdn/hisax/teles0.c linux-2.0.31-pre4/drivers/isdn/hisax/teles0.c
--- linux-2.0.31-pre3/drivers/isdn/hisax/teles0.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/hisax/teles0.c	2003-08-15 15:03:56.000000000 -0700
@@ -276,7 +276,7 @@
 
 		r = readhscx(sp->membase, hsp->hscx, HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
+			if (!(r & 0x80))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX invalid frame");
 			if ((r & 0x40) && hsp->mode)
@@ -285,7 +285,7 @@
 						hsp->mode);
 					debugl1(sp, tmp);
 				}
-			if (!r & 0x20)
+			if (!(r & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX CRC error");
 			writehscxCMDR(sp->membase, hsp->hscx, 0x80);
@@ -450,7 +450,7 @@
 			if (exval & 0x40)
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
+			if (!(exval & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC CRC error");
 			writeisac(sp->membase, ISAC_CMDR, 0x80);
diff -urN linux-2.0.31-pre3/drivers/isdn/hisax/teles3.c linux-2.0.31-pre4/drivers/isdn/hisax/teles3.c
--- linux-2.0.31-pre3/drivers/isdn/hisax/teles3.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/hisax/teles3.c	2003-08-15 15:03:56.000000000 -0700
@@ -252,7 +252,7 @@
 
 		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
+			if (!(r & 0x80))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX invalid frame");
 			if ((r & 0x40) && hsp->mode)
@@ -261,7 +261,7 @@
 						hsp->mode);
 					debugl1(sp, tmp);
 				}
-			if (!r & 0x20)
+			if (!(r & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "HSCX CRC error");
 			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
@@ -428,7 +428,7 @@
 			if (exval & 0x40)
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
+			if (!(exval & 0x20))
 				if (sp->debug & L1_DEB_WARN)
 					debugl1(sp, "ISAC CRC error");
 			writereg(sp->isac, ISAC_CMDR, 0x80);
diff -urN linux-2.0.31-pre3/drivers/isdn/icn/icn.c linux-2.0.31-pre4/drivers/isdn/icn/icn.c
--- linux-2.0.31-pre3/drivers/isdn/icn/icn.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/icn/icn.c	2003-08-15 15:03:56.000000000 -0700
@@ -1385,7 +1385,7 @@
 							}
 							current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
 							schedule();
-							sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n)02;EAZ%c\n",
+							sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
 								(a & 1)?'1':'C', (a & 2)?'2':'C');
 							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
 							printk(KERN_INFO
diff -urN linux-2.0.31-pre3/drivers/isdn/isdn_common.c linux-2.0.31-pre4/drivers/isdn/isdn_common.c
--- linux-2.0.31-pre3/drivers/isdn/isdn_common.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/isdn/isdn_common.c	2003-08-15 15:03:56.000000000 -0700
@@ -1661,39 +1661,38 @@
 					dev->infochain = (infostruct *) (p->next);
 				kfree(p);
 				return CLOSEVAL;
-				kfree(p);
 			}
 			q = p;
 			p = (infostruct *) (p->next);
 		}
-		return CLOSEVAL;
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+		return CLOSEVAL;
 	}
 	if (minor < ISDN_MINOR_CTRL) {
 		drvidx = isdn_minor2drv(minor);
-			return CLOSEVAL;
 		if (drvidx < 0)
+			return CLOSEVAL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		return CLOSEVAL;
 		(void) dev->drv[drvidx]->interface->command(&c);
+		return CLOSEVAL;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-			return CLOSEVAL;
 		if (drvidx < 0)
+			return CLOSEVAL;
 		if (dev->profd == current)
 			dev->profd = NULL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		return CLOSEVAL;
 		(void) dev->drv[drvidx]->interface->command(&c);
+		return CLOSEVAL;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
-	return CLOSEVAL;
 #endif
+	return CLOSEVAL;
 }
 
 static struct file_operations isdn_fops =
diff -urN linux-2.0.31-pre3/drivers/net/3c59x.c linux-2.0.31-pre4/drivers/net/3c59x.c
--- linux-2.0.31-pre3/drivers/net/3c59x.c	1997-04-08 08:47:45.000000000 -0700
+++ linux-2.0.31-pre4/drivers/net/3c59x.c	2003-08-15 15:03:56.000000000 -0700
@@ -1,31 +1,53 @@
-/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/* 3c900.c: A 3Com EtherLink III XL "Boomerang" ethernet driver for linux. */
 /*
-	Written 1995 by Donald Becker.
+	Written 1996-1997 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	This driver is for the 3Com "Vortex" series ethercards.  Members of
-	the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
-	EtherLink.
+	This driver is for the 3Com "Vortex" and "Boomerang" series ethercards.
+	Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597
+	and the EtherLink XL 3c900 and 3c905 cards.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 */
 
-static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
+static char *version =
+"3c59x.c/3c900.c:v0.42 7/15/97 Donald Becker linux-vortex@cesdis.gsfc.nasa.gov\n";
 
 /* "Knobs" that turn on special features. */
-/* Enable the experimental automatic media selection code. */
+/* Enable the automatic media selection code. */
 #define AUTOMEDIA 1
 
-/* Allow the use of bus master transfers instead of programmed-I/O for the
-   Tx process.  Bus master transfers are always disabled by default, but
-   iff this is set they may be turned on using 'options'. */
+/* Allow the use of fragment bus master transfers instead of only
+   programmed-I/O for Vortex cards.  Full-bus-master transfers are always
+   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
+   the feature may be turned on using 'options'. */
 #define VORTEX_BUS_MASTER
 
+/* A few values that may be tweaked. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE	16
+#define RX_RING_SIZE	32
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1512 effectively disables this feature. */
+static const rx_copybreak = 200;
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/version.h>
 #include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -39,6 +61,7 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/timer.h>
+#include <asm/irq.h>			/* For NR_IRQS only. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 
@@ -46,27 +69,64 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>		/* Redundant above, here for easy clean-up. */
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#if defined(__alpha)
+#error "The Alpha architecture is only support with kernel version 2.0."
+#endif
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else  /* 1.3.0 and later */
 #define RUN_AT(x) (jiffies + (x))
 #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
 
+#ifdef SA_SHIRQ
 #define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
 #define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
 #define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#else
+#define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
 
 /* "Knobs" for adjusting internal parameters. */
 /* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define VORTEX_DEBUG 2
+#define VORTEX_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+   debugging. */
+static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;
 
 /* Number of times to check to see if the Tx FIFO has space, used in some
    limited cases. */
 #define WAIT_TX_AVAIL 200
 
 /* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT  40		/* Time in jiffies before concluding Tx hung */
 
-/* The total size is twice that of the original EtherLinkIII series: the
-   runtime register window, window 1, is now always mapped in. */
+/* The Vortex size is twice that of the original EtherLinkIII series: the
+   runtime register window, window 1, is now always mapped in.
+   The Boomerang size is twice as large as the Vortex -- it has additional
+   bus master control registers. */
 #define VORTEX_TOTAL_SIZE 0x20
+#define BOOMERANG_TOTAL_SIZE 0x40
 
 #ifdef HAVE_DEVLIST
 struct netdev_entry tc59x_drv =
@@ -79,24 +139,37 @@
 int vortex_debug = 1;
 #endif
 
-static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+/* Caution!  These entries must be consistent, with the EISA ones last. */
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
+								0x9050, 0x9051, 0, 0};
 static const char *product_names[] = {
 	"3c590 Vortex 10Mbps",
 	"3c595 Vortex 100baseTX",
 	"3c595 Vortex 100baseT4",
 	"3c595 Vortex 100base-MII",
-	"EISA Vortex 3c597",
+	"3c900 Boomerang 10baseT",
+	"3c900 Boomerang 10Mbps/Combo",
+	"3c905 Boomerang 100baseTx",
+	"3c905 Boomerang 100baseT4",
+	"3c592 EISA 10mbps Demon/Vortex",
+	"3c597 EISA Fast Demon/Vortex",
 };
-#define DEMON_INDEX 5			/* Caution!  Must be consistent with above! */
+#define DEMON10_INDEX 8
+#define DEMON100_INDEX 9
 
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
-10/100baseT adapter.  It also works with the 3c590, a similar product
-with only a 10Mbs interface.
+This device driver is designed for the 3Com FastEtherLink and FastEtherLink
+XL, 3Com's PCI to 10/100baseT adapters.  It also works with the 10Mbs
+versions of the FastEtherLink cards.  The supported product IDs are
+  3c590, 3c592, 3c595, 3c597, 3c900, 3c905
+
+The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
+the kernel source or available from
+    cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
 
 II. Board-specific settings
 
@@ -112,12 +185,33 @@
 series.  The primary interface is two programmed-I/O FIFOs, with an
 alternate single-contiguous-region bus-master transfer (see next).
 
+The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
+lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
+DEC Tulip and Intel Speedo3.  The first chip version retains a compatible
+programmed-I/O interface that will be removed in the 'B' and subsequent
+revisions.
+
 One extension that is advertised in a very large font is that the adapters
-are capable of being bus masters.  Unfortunately this capability is only for
-a single contiguous region making it less useful than the list of transfer
-regions available with the DEC Tulip or AMD PCnet.  Given the significant
-performance impact of taking an extra interrupt for each transfer, using
-DMA transfers is a win only with large blocks.
+are capable of being bus masters.  On the Vortex chip this capability was
+only for a single contiguous region making it far less useful than the full
+bus master capability.  There is a significant performance impact of taking
+an extra interrupt or polling for the completion of each transfer, as well
+as difficulty sharing the single transfer engine between the transmit and
+receive threads.  Using DMA transfers is a win only with large blocks or
+with the flawed versions of the Intel Orion motherboard PCI controller.
+
+The Boomerang chip's full-bus-master interface is useful, and has the
+currently-unused advantages over other similar chips that queued transmit
+packets may be reordered and receive buffer groups are associated with a
+single frame.
+
+With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
+Tather than a fixed intermediate receive buffer, this scheme allocates
+full-sized skbuffs as receive buffers.  The value RX_COPYBREAK is used as
+the copying breakpoint: it is chosen to trade-off the memory wasted by
+passing the full-sized skbuff to the queue layer for all frames vs. the
+copying cost of copying a frame to a correctly-sized skbuff.
+
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -127,8 +221,8 @@
 
 IV. Notes
 
-Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
-3c590 and 3c595 boards.
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development
+3c590, 3c595, and 3c900 boards.
 The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
 the EISA version is called "Demon".  According to Terry these names come
 from rides at the local amusement park.
@@ -159,8 +253,10 @@
 
 enum vortex_cmd {
 	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
-	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
-	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11,
+	UpStall = 6<<11, UpUnstall = (6<<11)+1,
+	DownStall = (6<<11)+2, DownUnstall = (6<<11)+3,
+	RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
 	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
 	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
 	SetTxThreshold = 18<<11, SetTxStart = 19<<11,
@@ -175,7 +271,8 @@
 enum vortex_status {
 	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
 	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+	IntReq = 0x0040, StatsFull = 0x0080,
+	DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10,
 	DMAInProgress = 1<<11,			/* DMA controller is still busy.*/
 	CmdInProgress = 1<<12,			/* EL3_CMD is still busy.*/
 };
@@ -212,12 +309,12 @@
 		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
 		int pad8:8;
 		unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
-		int pad24:8;
+		int pad24:7;
 	} u;
 };
 
 enum Window4 {
-	Wn4_Media = 0x0A,		/* Window 4: Various transcvr/media bits. */
+	Wn4_NetDiag = 6, Wn4_Media = 10,		/* Window 4: Xcvr/media bits. */
 };
 enum Win4_Media_bits {
 	Media_SQE = 0x0008,		/* Enable SQE error counting for AUI. */
@@ -228,11 +325,43 @@
 enum Window7 {					/* Window 7: Bus Master control. */
 	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
 };
+/* Boomerang bus master control registers. */
+enum MasterCtrl {
+	PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c,
+	TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38,
+};
+
+/* The Rx and Tx descriptor lists.
+   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
+   alignment contraint on tx_ring[] and rx_ring[]. */
+struct boom_rx_desc {
+	u32 next;
+	s32 status;
+	u32 addr;
+	s32 length;
+};
+/* Values for the Rx status entry. */
+#define RX_COMPLETE 0x00008000
+
+struct boom_tx_desc {
+	u32 next;
+	s32 status;
+	u32 addr;
+	s32 length;
+};
 
 struct vortex_private {
 	char devname[8];			/* "ethN" string, also for kernel debug. */
 	const char *product_name;
 	struct device *next_module;
+	/* The Rx and Tx rings are here to keep them quad-word-aligned. */
+	struct boom_rx_desc rx_ring[RX_RING_SIZE];
+	struct boom_tx_desc tx_ring[TX_RING_SIZE];
+	/* The addresses of transmit- and receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
+	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	struct enet_statistics stats;
 	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */
 	struct timer_list timer;	/* Media selection timer. */
@@ -241,7 +370,12 @@
 	unsigned int available_media:8,	/* From Wn3_Options */
 	  media_override:3, 			/* Passed-in media type. */
 	  default_media:3,			/* Read from the EEPROM. */
-	  full_duplex:1, bus_master:1, autoselect:1;
+	  full_duplex:1, autoselect:1,
+	  bus_master:1,				/* Vortex can only do a fragment bus-m. */
+	  full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
+      tx_full:1;
+	u16 capabilities;			/* Adapter capabilities word. */
+	u16 info1, info2;			/* Software information information. */
 };
 
 /* The action to take with a media selection timer tick.
@@ -256,28 +390,34 @@
 } media_tbl[] = {
   {	"10baseT",   Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
   { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
-  { "undefined", 0,			0x80, 0 /* Undefined */, 0},
+  { "undefined", 0,			0x80, 0 /* Undefined */, 10000},
   { "10base2",   0,			0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
   { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
   { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
   { "MII",		 0,			0x40, 0 /* MII->10baseT */, (14*HZ)/10},
-  { "undefined", 0,			0x01, 0 /* Undefined/100baseT4 */, 0},
-  { "Default",	 0,			0xFF, 0 /* Use default */, 0},
+  { "undefined", 0,			0x01, 0 /* Undefined/100baseT4 */, 10000},
+  { "Default",	 0,			0xFF, 0 /* Use default */, 10000},
 };
 
 static int vortex_scan(struct device *dev);
-static int vortex_found_device(struct device *dev, int ioaddr, int irq,
-							   int product_index, int options);
+static struct device *vortex_found_device(struct device *dev, int ioaddr,
+										  int irq, int product_index,
+										  int options);
 static int vortex_probe1(struct device *dev);
 static int vortex_open(struct device *dev);
 static void vortex_timer(unsigned long arg);
 static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev);
 static int vortex_rx(struct device *dev);
+static int boomerang_rx(struct device *dev);
 static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
 static int vortex_close(struct device *dev);
 static void update_stats(int addr, struct device *dev);
 static struct enet_statistics *vortex_get_stats(struct device *dev);
 static void set_rx_mode(struct device *dev);
+#ifndef NEW_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 
 
 /* Unlike the other PCI cards the 59x cards don't need a large contiguous
@@ -297,6 +437,8 @@
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
 /* Note: this is the only limit on the number of cards supported!! */
 static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+/* Variables to work-around the Compaq PCI BIOS32 problem. */
+static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
 
 #ifdef MODULE
 static int debug = -1;
@@ -336,94 +478,141 @@
 {
 	int cards_found = 0;
 
+#ifndef NO_PCI					/* Allow an EISA-only driver. */
+	/* Ideally we would detect all cards in slot order.  That would
+	   be best done a central PCI probe dispatch, which wouldn't work
+	   well with the current structure.  So instead we detect 3Com cards
+	   in slot order. */
 	if (pcibios_present()) {
 		static int pci_index = 0;
-		static int board_index = 0;
-		for (; product_ids[board_index]; board_index++, pci_index = 0) {
-			for (; pci_index < 16; pci_index++) {
-				unsigned char pci_bus, pci_device_fn, pci_irq_line;
-				unsigned char pci_latency;
-				unsigned int pci_ioaddr;
-				unsigned short pci_command;
-
-				if (pcibios_find_device(TCOM_VENDOR_ID,
-										product_ids[board_index], pci_index,
-										&pci_bus, &pci_device_fn))
+		unsigned char pci_bus, pci_device_fn;
+
+		for (;pci_index < 0xff; pci_index++) {
+			unsigned char pci_irq_line, pci_latency;
+			unsigned short pci_command, vendor, device;
+			unsigned int pci_ioaddr;
+
+			int board_index = 0;
+			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
+									pci_index, &pci_bus, &pci_device_fn)
+				!= PCIBIOS_SUCCESSFUL)
+				break;
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_VENDOR_ID, &vendor);
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_DEVICE_ID, &device);
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+
+			if (vendor != TCOM_VENDOR_ID)
+				continue;
+
+			for (board_index = 0; product_ids[board_index]; board_index++) {
+				if (device == product_ids[board_index])
 					break;
-				pcibios_read_config_byte(pci_bus, pci_device_fn,
-										 PCI_INTERRUPT_LINE, &pci_irq_line);
-				pcibios_read_config_dword(pci_bus, pci_device_fn,
-										  PCI_BASE_ADDRESS_0, &pci_ioaddr);
-				/* Remove I/O space marker in bit 0. */
-				pci_ioaddr &= ~3;
+			}
+			if (product_ids[board_index] == 0) {
+				printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:"
+					   " not configured.\n", device);
+				continue;
+			}
+			if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE))
+				continue;
 
-#ifdef VORTEX_BUS_MASTER
+			dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
+									  board_index, dev && dev->mem_start
+									  ? dev->mem_start : options[cards_found]);
+
+			if (dev) {
 				/* Get and check the bus-master and latency values.
 				   Some PCI BIOSes fail to set the master-enable bit, and
 				   the latency timer must be set to the maximum value to avoid
 				   data corruption that occurs when the timer expires during
-				   a transfer.  Yes, it's a bug. */
+				   a transfer -- a bug in the Vortex chip. */
 				pcibios_read_config_word(pci_bus, pci_device_fn,
 										 PCI_COMMAND, &pci_command);
 				if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-					printk("  PCI Master Bit has not been set! Setting...\n");
+					printk("%s:  PCI Master Bit has not been set! "
+						   " Setting...\n", dev->name);
 					pci_command |= PCI_COMMAND_MASTER;
 					pcibios_write_config_word(pci_bus, pci_device_fn,
 											  PCI_COMMAND, pci_command);
 				}
 				pcibios_read_config_byte(pci_bus, pci_device_fn,
 										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency != 255) {
-					printk("  Overriding PCI latency timer (CFLT) setting of"
-						   " %d, new value is 255.\n", pci_latency);
+				if (pci_latency != 248) {
+					printk("%s: Overriding PCI latency"
+						   " timer (CFLT) setting of %d, new value is 248.\n",
+						   dev->name, pci_latency);
 					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, 255);
+											  PCI_LATENCY_TIMER, 248);
 				}
-#endif  /* VORTEX_BUS_MASTER */
-				vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index,
-									dev && dev->mem_start ? dev->mem_start
-									: options[cards_found]);
 				dev = 0;
 				cards_found++;
 			}
 		}
 	}
+#endif /* NO_PCI */
 
 	/* Now check all slots of the EISA bus. */
 	if (EISA_bus) {
 		static int ioaddr = 0x1000;
 		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+			int product_id, product_index;
+			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
+				continue;
 			/* Check the standard EISA ID register for an encoded '3Com'. */
 			if (inw(ioaddr + 0xC80) != 0x6d50)
 				continue;
 			/* Check for a product that we support, 3c59{2,7} any rev. */
-			if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 		/* 597 */
-				&& (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059)	/* 592 */
+			product_id = inw(ioaddr + 0xC82) & 0xF0FF;
+			if (product_id == 0x7059) 		/* 597 */
+				product_index = DEMON100_INDEX;
+			else if (product_id == 0x2059)	/* 592 */
+				product_index = DEMON10_INDEX;
+			else
 				continue;
 			vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-								DEMON_INDEX,  dev && dev->mem_start
+								product_index,  dev && dev->mem_start
 								? dev->mem_start : options[cards_found]);
 			dev = 0;
 			cards_found++;
 		}
 	}
 
+	/* Special code to work-around the Compaq PCI BIOS32 problem. */
+	if (compaq_ioaddr) {
+		vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
+							dev && dev->mem_start ? dev->mem_start : options[cards_found]);
+		cards_found++;
+		dev = 0;
+	}
+
+	/* Finally check for a 3c515 on the ISA bus. */
+	/* (3c515 support omitted on this version.) */
+
 	return cards_found;
 }
 
-static int vortex_found_device(struct device *dev, int ioaddr, int irq,
-							   int product_index, int options)
+static struct device *
+vortex_found_device(struct device *dev, int ioaddr, int irq,
+					int product_index, int options)
 {
 	struct vortex_private *vp;
 
 #ifdef MODULE
 	/* Allocate and fill new device structure. */
 	int dev_size = sizeof(struct device) +
-		sizeof(struct vortex_private);
-	
+		sizeof(struct vortex_private) + 15;		/* Pad for alignment */
+
 	dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
 	memset(dev, 0, dev_size);
-	dev->priv = ((void *)dev) + sizeof(struct device);
+	/* Align the Rx and Tx ring entries.  */
+	dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
 	vp = (struct vortex_private *)dev->priv;
 	dev->name = vp->devname; /* An empty string. */
 	dev->base_addr = ioaddr;
@@ -444,9 +633,10 @@
 	vp->next_module = root_vortex_dev;
 	root_vortex_dev = dev;
 	if (register_netdev(dev) != 0)
-		return -EIO;
+		return 0;
 #else  /* not a MODULE */
 	if (dev) {
+		/* Caution: quad-word alignment required for rings! */
 		dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
 		memset(dev->priv, 0, sizeof (struct vortex_private));
 	}
@@ -468,13 +658,14 @@
 
 	vortex_probe1(dev);
 #endif /* MODULE */
-	return 0;
+	return dev;
 }
 
 static int vortex_probe1(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
 	int i;
 
 	printk("%s: 3Com %s at %#3x,", dev->name,
@@ -482,27 +673,33 @@
 
 	/* Read the station address from the EEPROM. */
 	EL3WINDOW(0);
-	for (i = 0; i < 3; i++) {
+	for (i = 0; i < 0x18; i++) {
 		short *phys_addr = (short *)dev->dev_addr;
 		int timer;
-		outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
 		/* Pause for at least 162 us. for the read to take place. */
-		for (timer = 162*4 + 400; timer >= 0; timer--) {
-			SLOW_DOWN_IO;
+		for (timer = 4; timer >= 0; timer--) {
+			udelay(162);
 			if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
 				break;
 		}
-		phys_addr[i] = htons(inw(ioaddr + Wn0EepromData));
+		eeprom[i] = inw(ioaddr + Wn0EepromData);
+		checksum ^= eeprom[i];
+		if (i >= 10 && i < 13)
+			phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData));
 	}
+	checksum = (checksum ^ (checksum >> 8)) & 0xff;
+	if (checksum != 0x00)
+		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
 	for (i = 0; i < 6; i++)
 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
 	printk(", IRQ %d\n", dev->irq);
 	/* Tell them about an invalid IRQ. */
-	if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
-		printk(" *** Warning: this IRQ is unlikely to work!\n");
+	if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
+		printk(" *** Warning: this IRQ is unlikely to work! ***\n");
 
 	{
-		char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+		char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
 		union wn3_config config;
 		EL3WINDOW(3);
 		vp->available_media = inw(ioaddr + Wn3_Options);
@@ -520,8 +717,23 @@
 		vp->default_media = config.u.xcvr;
 		vp->autoselect = config.u.autoselect;
 	}
+	if (vp->media_override != 7) {
+		printk("  Media override to transceiver type %d (%s).\n",
+			   vp->media_override, media_tbl[vp->media_override].name);
+		dev->if_port = vp->media_override;
+	}
 
-	/* We do a request_region() only to register /proc/ioports info. */
+	vp->info1 = eeprom[13];
+	vp->info2 = eeprom[15];
+	vp->capabilities = eeprom[16];
+	if (vp->capabilities & 0x20) {
+	  vp->full_bus_master_tx = 0;	/* TX bugs, force bus_master_tx to 0? */
+	  printk("  Enabling bus-master transmits and %s receives.\n",
+			 (vp->info2 & 1) ? "early" : "whole-frame" );
+	  vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
+	}
+
+	/* We do a request_region() to register /proc/ioports info. */
 	request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
 
 	/* The 3c59x-specific entries in the device structure. */
@@ -529,7 +741,11 @@
 	dev->hard_start_xmit = &vortex_start_xmit;
 	dev->stop = &vortex_close;
 	dev->get_stats = &vortex_get_stats;
+#ifdef NEW_MULTICAST
 	dev->set_multicast_list = &set_rx_mode;
+#else
+	dev->set_multicast_list = &set_multicast_list;
+#endif
 
 	return 0;
 }
@@ -545,8 +761,6 @@
 
 	/* Before initializing select the active media port. */
 	EL3WINDOW(3);
-	if (vp->full_duplex)
-		outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
 	config.i = inl(ioaddr + Wn3_Config);
 
 	if (vp->media_override != 7) {
@@ -576,6 +790,10 @@
 	config.u.xcvr = dev->if_port;
 	outl(config.i, ioaddr + Wn3_Config);
 
+	/* Set the full-duplex bit. */
+	outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+		 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+
 	if (vortex_debug > 1) {
 		printk("%s: vortex_open() InternalConfig %8.8x.\n",
 			dev->name, config.i);
@@ -583,22 +801,32 @@
 
 	outw(TxReset, ioaddr + EL3_CMD);
 	for (i = 20; i >= 0 ; i--)
-		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 			break;
 
 	outw(RxReset, ioaddr + EL3_CMD);
 	/* Wait a few ticks for the RxReset command to complete. */
 	for (i = 20; i >= 0 ; i--)
-		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 			break;
 
 	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
 
+#ifdef SA_SHIRQ
 	/* Use the now-standard shared IRQ implementation. */
 	if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ,
 					vp->product_name, dev)) {
 		return -EAGAIN;
 	}
+#else
+	if (dev->irq == 0  ||  irq2dev_map[dev->irq] != NULL)
+		return -EAGAIN;
+	irq2dev_map[dev->irq] = dev;
+	if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+		irq2dev_map[dev->irq] = NULL;
+		return -EAGAIN;
+	}
+#endif
 
 	if (vortex_debug > 1) {
 		EL3WINDOW(4);
@@ -623,18 +851,52 @@
 	/* Switch to the stats window, and clear all stats by reading. */
 	outw(StatsDisable, ioaddr + EL3_CMD);
 	EL3WINDOW(6);
-	for (i = 0; i < 10; i++)	
+	for (i = 0; i < 10; i++)
 		inb(ioaddr + i);
 	inw(ioaddr + 10);
 	inw(ioaddr + 12);
 	/* New: On the Vortex we must also clear the BadSSD counter. */
 	EL3WINDOW(4);
 	inb(ioaddr + 12);
+	/* ..and on the Boomerang we enable the extra statistics bits. */
+	outw(0x0040, ioaddr + Wn4_NetDiag);
 
 	/* Switch to register set 7 for normal use. */
 	EL3WINDOW(7);
 
-	/* Set receiver mode: presumably accept b-case and phys addr only. */
+	if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+		vp->cur_rx = vp->dirty_rx = 0;
+		/* Initialize the RxEarly register as recommended. */
+		outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD);
+		outl(0x0020, ioaddr + PktStatus);
+		if (vortex_debug > 2)
+			printk("%s:  Filling in the Rx ring.\n", dev->name);
+		for (i = 0; i < RX_RING_SIZE; i++) {
+			struct sk_buff *skb;
+			vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
+			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
+			vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
+			skb = dev_alloc_skb(PKT_BUF_SZ);
+			vp->rx_skbuff[i] = skb;
+			if (skb == NULL)
+				break;			/* Bad news!  */
+			skb->dev = dev;			/* Mark as being used by this device. */
+			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+			vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+		}
+		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
+	}
+	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */
+		dev->hard_start_xmit = &boomerang_start_xmit;
+		vp->cur_tx = vp->dirty_tx = 0;
+		outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+		/* Clear the Tx ring. */
+		for (i = 0; i < TX_RING_SIZE; i++)
+			vp->tx_skbuff[i] = 0;
+		outl(0, ioaddr + DownListPtr);
+	}
+	/* Set reciever mode: presumably accept b-case and phys addr only. */
 	set_rx_mode(dev);
 	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
 
@@ -645,12 +907,17 @@
 	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
 	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
 	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+	outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
+		 (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+		 (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+		 (vp->bus_master ? DMADone : 0),
+		 ioaddr + EL3_CMD);
 	/* Ack all pending events, and set active indicator mask. */
 	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
 		 ioaddr + EL3_CMD);
 	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-		 | DMADone, ioaddr + EL3_CMD);
+		 | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+			ioaddr + EL3_CMD);
 
 	MOD_INC_USE_COUNT;
 
@@ -685,7 +952,7 @@
 		} else if (vortex_debug > 1)
 		  printk("%s: Media %s is has no link beat, %x.\n",
 				   dev->name, media_tbl[dev->if_port].name, media_status);
- 
+
 		break;
 	  default:					/* Other media types handled by Tx timeouts. */
 		if (vortex_debug > 1)
@@ -731,72 +998,113 @@
 	return;
 }
 
-static int
-vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+static void vortex_tx_timeout(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int ioaddr = dev->base_addr;
+	int i;
 
-	/* Part of the following code is inspired by code from Giuseppe Ciaccio,
-	   ciaccio@disi.unige.it.
-	   It works around a ?bug? in the 8K Vortex that only occurs on some
-	   systems: the TxAvailable interrupt seems to be lost.
-	   The ugly work-around is to busy-wait for room available in the Tx
-	   buffer before deciding the transmitter is actually hung.
-	   This busy-wait should never really occur, since the problem is that
-	   there actually *is*  room in the Tx FIFO.
-
-	   This pointed out an optimization -- we can ignore dev->tbusy if
-	   we actually have room for this packet.
-	   */
-
-#if 0
-	/* unstable optimization */
-	if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */
-	  dev->tbusy = 0;			/* Fake out the check below. */
-	else
-#endif
-	if (dev->tbusy) {
-		/* Transmitter timeout, serious problems. */
-		int tickssofar = jiffies - dev->trans_start;
-		int i;
-
-		if (tickssofar < 2)		/* We probably aren't empty. */
-			return 1;
-		/* Wait a while to see if there really is room. */
-		for (i = WAIT_TX_AVAIL; i >= 0; i--)
-			if (inw(ioaddr + TxFree) > skb->len)
-			  break;
-		if ( i < 0) {
-			if (tickssofar < TX_TIMEOUT)
-				return 1;
-			printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
-				   dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
-			/* Issue TX_RESET and TX_START commands. */
-			outw(TxReset, ioaddr + EL3_CMD);
-			for (i = 20; i >= 0 ; i--)
-				if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
-						break;
-			outw(TxEnable, ioaddr + EL3_CMD);
-			dev->trans_start = jiffies;
-			dev->tbusy = 0;
-			vp->stats.tx_errors++;
-			vp->stats.tx_dropped++;
-			dev_kfree_skb(skb, FREE_WRITE);
-			return 0;			/* Yes, silently *drop* the packet! */
+	printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+		   dev->name, inb(ioaddr + TxStatus),
+		   inw(ioaddr + EL3_STATUS));
+	/* Slight code bloat to be user friendly. */
+	if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
+	  printk("%s: Transmitter encountered 16 collisions -- network"
+			 " network cable problem?\n", dev->name);
+#ifndef final_version
+	printk("  Flags; bus-master %d, full %d; dirty %d current %d.\n",
+		   vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx);
+	printk("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
+		   &vp->tx_ring[0]);
+	for (i = 0; i < TX_RING_SIZE; i++) {
+	  printk("  %d: %p  length %8.8x status %8.8x\n", i,
+			 &vp->tx_ring[i],
+			 vp->tx_ring[i].length,
+			 vp->tx_ring[i].status);
+	}
+	if (vp->full_bus_master_rx) {
+		printk("  Switching to non-bus-master receives.\n");
+		outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
+			 (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+			 RxComplete | (vp->bus_master ? DMADone : 0),
+			 ioaddr + EL3_CMD);
+	}
+#endif
+	/* Issue TX_RESET and TX_START commands. */
+	outw(TxReset, ioaddr + EL3_CMD);
+	for (i = 20; i >= 0 ; i--)
+	  if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+		break;
+	if (vp->full_bus_master_tx) {
+	  /*  Change 6/25/97 Michael Sievers sieversm@mail.desy.de
+              The card has been resetted, but the Tx Ring is still full.
+		 Since the card won't know where to resume, 'update' the
+		 Tx Ring. Probably, we'll lose 16 packets this way, these
+		 will be accounted for as 'dropped'. The code to update the
+		 Tx Ring is taken from the Interrupt handler. */
+   
+	  unsigned int dirty_tx = vp->dirty_tx;
+	  
+	  while (vp->cur_tx - dirty_tx > 0) {
+		int entry = dirty_tx % TX_RING_SIZE;
+		if (inl(ioaddr + DownListPtr) ==
+			virt_to_bus(&vp->tx_ring[entry]))
+		  break;			/* It still hasn't been processed. */
+		if (vp->tx_skbuff[entry]) {
+		  if (vortex_debug > 0)
+			printk("%s: Freeing Tx ring entry %d\n",dev->name,entry);
+		  dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE);
+		  vp->tx_skbuff[entry] = 0;
 		}
+		vp->stats.tx_errors++;
+		vp->stats.tx_dropped++;
+		dirty_tx++;
+	  }
+	  vp->dirty_tx = dirty_tx;
+	  vp->tx_full= 0;
+	} else {     /* not bus-master, no Tx ring to clear */
+	  vp->stats.tx_errors++;
+	  vp->stats.tx_dropped++;
+	}
+	
+	/* Issue Tx Enable */
+	outw(TxEnable, ioaddr + EL3_CMD);
+	dev->trans_start = jiffies;
+	
+	/* Switch to register set 7 for normal use. */
+	EL3WINDOW(7);
+	
+	/* The TxFreeThreshold has to be set again after a reset! */
+	if (vp->full_bus_master_tx) {  
+	  outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+	  /* This is to be sure that all bus-master Tx features
+		 are correctly re-initialized after a reset, although
+		 the DownListPtr should already be 0 at this point. */
+	  outl(0, ioaddr + DownListPtr);
 	}
+	/* finally, allow new Transmits */
+	dev->tbusy = 0;
+	/* End of Michael Sievers <sieversm@mail.desy.de> changes. */
+}
 
-	if(skb == NULL) {
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
 		dev_tint(dev);
-		return NULL;
+		return 0;
 	}
+#endif
 
-	/* 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 this ever occurs the queue layer is doing something evil! */
-	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start >= TX_TIMEOUT)
+			vortex_tx_timeout(dev);
 		return 1;
 	}
 
@@ -849,7 +1157,7 @@
 					int j;
 					outw(TxReset, ioaddr + EL3_CMD);
 					for (j = 20; j >= 0 ; j--)
-						if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+						if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 							break;
 				}
 				outw(TxEnable, ioaddr + EL3_CMD);
@@ -860,12 +1168,84 @@
 	return 0;
 }
 
+static int
+boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+#endif
+
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start >= TX_TIMEOUT)
+			vortex_tx_timeout(dev);
+		return 1;
+	} else {
+		/* Calculate the next Tx descriptor entry. */
+		int entry = vp->cur_tx % TX_RING_SIZE;
+		struct boom_tx_desc *prev_entry =
+			&vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE];
+		unsigned long flags, i;
+
+		if (vortex_debug > 3)
+			printk("%s: Trying to send a packet, Tx index %d.\n",
+				   dev->name, vp->cur_tx);
+		if (vp->tx_full) {
+		  if (vortex_debug >0)
+			printk("%s: Tx Ring full, refusing to send buffer.\n",
+				   dev->name);
+		  return 1;
+		} 
+		/* end change 06/25/97 M. Sievers */	
+		vp->tx_skbuff[entry] = skb;
+		vp->tx_ring[entry].next = 0;
+		vp->tx_ring[entry].addr = virt_to_bus(skb->data);
+		vp->tx_ring[entry].length = skb->len | 0x80000000;
+		vp->tx_ring[entry].status = skb->len | 0x80000000;
+
+		save_flags(flags);
+		cli();
+		outw(DownStall, ioaddr + EL3_CMD);
+		/* Wait for the stall to complete. */
+		for (i = 20; i >= 0 ; i--)
+			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
+				break;
+		prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
+		if (inl(ioaddr + DownListPtr) == 0) {
+			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
+			queued_packet++;
+		}
+		outw(DownUnstall, ioaddr + EL3_CMD);
+		restore_flags(flags);
+
+		vp->cur_tx++;
+		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
+			vp->tx_full = 1;
+		else {					/* Clear previous interrupt enable. */
+			prev_entry->status &= ~0x80000000;
+			dev->tbusy = 0;
+		}
+		dev->trans_start = jiffies;
+		return 0;
+	}
+}
+
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
 static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
 {
-	/* Use the now-standard shared IRQ implementation. */
+#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
 	struct device *dev = dev_id;
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
 	struct vortex_private *lp;
 	int ioaddr, status;
 	int latency;
@@ -911,13 +1291,41 @@
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
 		}
+		if (status & DownComplete) {
+			unsigned int dirty_tx = lp->dirty_tx;
+
+			while (lp->cur_tx - dirty_tx > 0) {
+				int entry = dirty_tx % TX_RING_SIZE;
+				if (inl(ioaddr + DownListPtr) ==
+					virt_to_bus(&lp->tx_ring[entry]))
+					break;			/* It still hasn't been processed. */
+				if (lp->tx_skbuff[entry]) {
+					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+					lp->tx_skbuff[entry] = 0;
+				}
+				lp->stats.tx_packets++;
+				dirty_tx++;
+			}
+			outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
+			if (lp->tx_full) {
+				lp->tx_full= 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+			lp->dirty_tx = dirty_tx;
+		}
 #ifdef VORTEX_BUS_MASTER
 		if (status & DMADone) {
 			outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
 			dev->tbusy = 0;
+			dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */
 			mark_bh(NET_BH);
 		}
 #endif
+		if (status & UpComplete) {
+			boomerang_rx(dev);
+			outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+		}
 		if (status & (AdapterFailure | RxEarly | StatsFull)) {
 			/* Handle all uncommon interrupts at once. */
 			if (status & RxEarly) {				/* Rx early is unused. */
@@ -943,7 +1351,8 @@
 							printk(" %2.2x", inb(ioaddr+reg));
 					}
 					EL3WINDOW(7);
-					outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+					outw(SetIntrEnb | TxAvailable | RxComplete
+						 | UpComplete | DownComplete, ioaddr + EL3_CMD);
 					DoneDidThat++;
 				}
 			}
@@ -960,10 +1369,10 @@
 		if (++i > 10) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.  "
 				   "Disabling functions (%4.4x).\n",
-				   dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+				   dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
 			/* Disable all pending interrupts. */
-			outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
+			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
 			break;
 		}
 		/* Acknowledge the IRQ. */
@@ -992,7 +1401,7 @@
 	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
 		if (rx_status & 0x4000) { /* Error, update stats. */
 			unsigned char rx_error = inb(ioaddr + RxErrors);
-			if (vortex_debug > 4)
+			if (vortex_debug > 2)
 				printk(" Rx error: status %2.2x.\n", rx_error);
 			vp->stats.rx_errors++;
 			if (rx_error & 0x01)  vp->stats.rx_over_errors++;
@@ -1011,28 +1420,36 @@
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
+#if LINUX_VERSION_CODE >= 0x10300
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
 					 (pkt_len + 3) >> 2);
+				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
+#else
+				skb->len = pkt_len;
+				/* 'skb->data' points to the start of sk_buff data area. */
+				insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2);
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+#endif  /* KERNEL_1_3_0 */
+				netif_rx(skb);
+				dev->last_rx = jiffies;
+				vp->stats.rx_packets++;
 				/* Wait a limited time to go to next packet. */
 				for (i = 200; i >= 0; i--)
-					if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 						break;
-				vp->stats.rx_packets++;
 				continue;
 			} else if (vortex_debug)
 				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
 					   dev->name, pkt_len);
 		}
-		vp->stats.rx_dropped++;
 		outw(RxDiscard, ioaddr + EL3_CMD);
+		vp->stats.rx_dropped++;
 		/* Wait a limited time to skip this packet. */
 		for (i = 200; i >= 0; i--)
-			if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 				break;
 	}
 
@@ -1040,17 +1457,111 @@
 }
 
 static int
+boomerang_rx(struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int entry = vp->cur_rx % RX_RING_SIZE;
+	int ioaddr = dev->base_addr;
+	int rx_status;
+
+	if (vortex_debug > 5)
+		printk("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
+			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+	while ((rx_status = vp->rx_ring[entry].status) & RX_COMPLETE) {
+		if (rx_status & 0x4000) { /* Error, update stats. */
+			unsigned char rx_error = rx_status >> 16;
+			if (vortex_debug > 4)
+				printk(" Rx error: status %2.2x.\n", rx_error);
+			vp->stats.rx_errors++;
+			if (rx_error & 0x01)  vp->stats.rx_over_errors++;
+			if (rx_error & 0x02)  vp->stats.rx_length_errors++;
+			if (rx_error & 0x04)  vp->stats.rx_frame_errors++;
+			if (rx_error & 0x08)  vp->stats.rx_crc_errors++;
+			if (rx_error & 0x10)  vp->stats.rx_length_errors++;
+		} else {
+			/* The packet length: up to 4.5K!. */
+			short pkt_len = rx_status & 0x1fff;
+			struct sk_buff *skb;
+
+			if (vortex_debug > 4)
+				printk("Receiving packet size %d status %4.4x.\n",
+					   pkt_len, rx_status);
+
+			/* Check if the packet is long enough to just accept without
+			   copying to a properly sized skbuff. */
+			if (pkt_len < rx_copybreak
+				&& (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+				/* 'skb_put()' points to the start of sk_buff data area. */
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(vp->rx_ring[entry].addr),
+					   pkt_len);
+				rx_copy++;
+			} else{
+				void *temp;
+				/* Pass up the skbuff already on the Rx ring. */
+				skb = vp->rx_skbuff[entry];
+				vp->rx_skbuff[entry] = NULL;
+				temp = skb_put(skb, pkt_len);
+				/* Remove this checking code for final release. */
+				if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
+					printk("%s: Warning -- the skbuff addresses do not match"
+						   " in boomerang_rx: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(vp->rx_ring[entry].addr),
+						   skb->head, temp);
+				rx_nocopy++;
+			}
+#if LINUX_VERSION_CODE > 0x10300
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+			skb->len = pkt_len;
+#endif
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			vp->stats.rx_packets++;
+		}
+		entry = (++vp->cur_rx) % RX_RING_SIZE;
+	}
+	/* Refill the Rx ring buffers. */
+	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+		struct sk_buff *skb;
+		entry = vp->dirty_rx % RX_RING_SIZE;
+		if (vp->rx_skbuff[entry] == NULL) {
+			skb = dev_alloc_skb(PKT_BUF_SZ);
+			if (skb == NULL)
+				break;			/* Bad news!  */
+			skb->dev = dev;			/* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+			vp->rx_ring[entry].addr = virt_to_bus(skb->tail);
+#else
+			vp->rx_ring[entry].addr = virt_to_bus(skb->data);
+#endif
+			vp->rx_skbuff[entry] = skb;
+		}
+		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
+	}
+	return 0;
+}
+
+static int
 vortex_close(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int ioaddr = dev->base_addr;
+	int i;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 
-	if (vortex_debug > 1)
+	if (vortex_debug > 1) {
 		printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
 			   dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+		printk("%s: vortex close stats: rx_nocopy %d rx_copy %d"
+			   " tx_queued %d.\n",
+			   dev->name, rx_nocopy, rx_copy, queued_packet);
+	}
 
 	del_timer(&vp->timer);
 
@@ -1065,9 +1576,36 @@
 		/* Turn off thinnet power.  Green! */
 		outw(StopCoax, ioaddr + EL3_CMD);
 
-	FREE_IRQ(dev->irq, dev);
+#ifdef SA_SHIRQ
+	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
+
+	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
 
 	update_stats(ioaddr, dev);
+	if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
+		outl(0, ioaddr + UpListPtr);
+		for (i = 0; i < RX_RING_SIZE; i++)
+			if (vp->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+				vp->rx_skbuff[i]->free = 1;
+#endif
+				dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE);
+				vp->rx_skbuff[i] = 0;
+			}
+	}
+	if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
+		outl(0, ioaddr + DownListPtr);
+		for (i = 0; i < TX_RING_SIZE; i++)
+			if (vp->tx_skbuff[i]) {
+				dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE);
+				vp->tx_skbuff[i] = 0;
+			}
+	}
+
 	MOD_DEC_USE_COUNT;
 
 	return 0;
@@ -1079,10 +1617,12 @@
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
-	update_stats(dev->base_addr, dev);
-	restore_flags(flags);
+	if (dev->start) {
+		save_flags(flags);
+		cli();
+		update_stats(dev->base_addr, dev);
+		restore_flags(flags);
+	}
 	return &vp->stats;
 }
 
@@ -1140,11 +1680,19 @@
 		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
 	} else	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
 		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
-	} else 
+	} else
 		new_mode = SetRxFilter | RxStation | RxBroadcast;
 
 	outw(new_mode, ioaddr + EL3_CMD);
 }
+#ifndef NEW_MULTICAST
+/* The old interface to set the Rx mode. */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+	set_rx_mode(dev);
+}
+#endif
 
 #ifdef MODULE
 void
@@ -1166,7 +1714,7 @@
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
  *  c-indent-level: 4
  *  tab-width: 4
  * End:
diff -urN linux-2.0.31-pre3/drivers/net/Config.in linux-2.0.31-pre4/drivers/net/Config.in
--- linux-2.0.31-pre3/drivers/net/Config.in	1997-03-06 10:03:51.000000000 -0800
+++ linux-2.0.31-pre4/drivers/net/Config.in	2003-08-15 15:03:56.000000000 -0700
@@ -60,6 +60,7 @@
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
     tristate 'SMC Ultra support' CONFIG_ULTRA
+    tristate 'SMC Ultra32 support' CONFIG_ULTRA32
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
diff -urN linux-2.0.31-pre3/drivers/net/hp100.c linux-2.0.31-pre4/drivers/net/hp100.c
--- linux-2.0.31-pre3/drivers/net/hp100.c	1996-04-11 23:49:38.000000000 -0700
+++ linux-2.0.31-pre4/drivers/net/hp100.c	2003-08-15 15:03:56.000000000 -0700
@@ -1,89 +1,66 @@
 /*
- * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
- *
- * Author:  Jaroslav Kysela, <perex@pf.jcu.cz>
- *
- * Supports only the following Hewlett Packard cards:
- *
- *  	HP J2577	10/100 EISA card with REVA Cascade chip
- *	HP J2573	10/100 ISA card with REVA Cascade chip
- *	HP 27248B	10 only EISA card with Cascade chip
- *	HP J2577	10/100 EISA card with Cascade chip
- *	HP J2573	10/100 ISA card with Cascade chip
- *	HP J2585	10/100 PCI card
- *
- * Other ATT2MD01 Chip based boards might be supported in the future
- * (there are some minor changes needed).
- *
- * This driver is based on the 'hpfepkt' crynwr packet driver.
- *
- * This source/code is public free; you can distribute it and/or modify 
- * it under terms of the GNU General Public License (published by the
- * Free Software Foundation) either version two of this License, or any 
- * later version.
- * ----------------------------------------------------------------------------
- *
- * Note: Some routines (interrupt handling, transmit) assumes that  
- *       there is the PERFORMANCE page selected...
- *
- * ----------------------------------------------------------------------------
- *
- * If you are going to use the module version of this driver, you may
- * change this values at the "insert time" :
- *
- *   Variable                   Description
- *
- *   hp100_rx_ratio		Range 1-99 - onboard memory used for RX
- *                              packets in %.
- *   hp100_priority_tx		If this variable is nonzero - all outgoing
- *                              packets will be transmitted as priority.
- *   hp100_port			Adapter port (for example 0x380).
- *
- * ----------------------------------------------------------------------------
- * MY BEST REGARDS GOING TO:
- *
- * IPEX s.r.o which lend me two HP J2573 cards and
- * the HP AdvanceStack 100VG Hub-15 for debugging.
- *
- * Russel Nellson <nelson@crynwr.com> for help with obtaining sources
- * of the 'hpfepkt' packet driver.
- *
- * Also thanks to Abacus Electric s.r.o which let me to use their 
- * motherboard for my second computer.
- *
- * ----------------------------------------------------------------------------
- *
- * TO DO:
- * ======
- *       - ioctl handling - some runtime setup things
- *       - 100Mb/s Voice Grade AnyLAN network adapter/hub services support
- *		- 802.5 frames
- *		- promiscuous mode
- *		- bridge mode
- *		- cascaded repeater mode
- *		- 100Mbit MAC
- *
- * Revision history:
- * =================
- * 
- *    Version   Date	    Description
- *
- *	0.1	14-May-95   Initial writing. ALPHA code was released.
- *                          Only HP J2573 on 10Mb/s (two machines) tested.
- *      0.11    14-Jun-95   Reset interface bug fixed?
- *			    Little bug in hp100_close function fixed.
- *                          100Mb/s connection debugged.
- *      0.12    14-Jul-95   Link down is now handled better.
- *      0.20    01-Aug-95   Added PCI support for HP J2585A card.
- *                          Statistics bug fixed.
- *      0.21    04-Aug-95   Memory mapped access support for PCI card.
- *                          Added priority transmit support for 100Mb/s
- *                          Voice Grade AnyLAN network.
- *
- */
+** hp100.c 
+** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
+**
+** $Id: hp100.c,v 1.54 1997/06/12 10:37:07 perex Exp perex $
+**
+** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
+** Extended for new busmaster capable chipsets by 
+** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
+**
+** Maintained by: Jaroslav Kysela <perex@jcu.cz>
+** 
+** This driver has only been tested with
+** -- HP J2585B 10/100 Mbit/s PCI Busmaster
+** -- HP J2585A 10/100 Mbit/s PCI 
+** -- HP J2970  10 Mbit/s PCI Combo 10base-T/BNC
+** -- HP J2973  10 Mbit/s PCI 10base-T
+** -- HP J2573  10/100 ISA
+** -- Compex ReadyLink ENET100-VG4  10/100 Mbit/s PCI / EISA
+** 
+** but it should also work with the other CASCADE based adapters.
+**
+** TODO:
+**       -  J2573 seems to hang sometimes when in shared memory mode.
+**       -  Mode for Priority TX
+**       -  Check PCI registers, performance might be improved?
+**       -  To reduce interrupt load in busmaster, one could switch off
+**          the interrupts that are used to refill the queues whenever the
+**          queues are filled up to more than a certain threshold.
+**
+**
+** This source/code is public free; you can distribute it and/or modify 
+** it under terms of the GNU General Public License (published by the
+** Free Software Foundation) either version two of this License, or any 
+** later version.
+**
+** 1.53 -> 1.54
+**   - added hardware multicast filter support (doesn't work)
+**   - little changes in hp100_sense_lan routine 
+**     - added support for Coax and AUI (J2970)
+**   - fix for multiple cards and hp100_mode parameter (insmod)
+**   - fix for shared IRQ 
+**
+** 1.52 -> 1.53
+**   - fixed bug in multicast support
+**
+*/
+
+#define HP100_DEFAULT_PRIORITY_TX 0 
+
+#undef HP100_DEBUG
+#undef HP100_DEBUG_B           /* Trace  */
+#undef HP100_DEBUG_BM          /* Debug busmaster code (PDL stuff) */
+
+#undef HP100_DEBUG_TRAINING    /* Debug login-to-hub procedure */
+#undef HP100_DEBUG_TX   
+#undef HP100_DEBUG_IRQ 
+#undef HP100_DEBUG_RX 
 
-#include <linux/module.h>
+#undef HP100_MULTICAST_FILTER  /* Need to be debugged... */
 
+#include <linux/version.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -101,7 +78,23 @@
 #include <linux/skbuff.h>
 
 #include <linux/types.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>  /* for CONFIG_PCI */
+#include <linux/delay.h>
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+typedef struct enet_statistics hp100_stats_t;
+#else
+#define LINUX_2_1
+typedef struct net_device_stats hp100_stats_t;
+#endif
+
+#ifndef __initfunc
+#define __initfunc(__initarg) __initarg
+#else
+#include <linux/init.h>
+#endif
 
 #include "hp100.h"
 
@@ -109,18 +102,28 @@
  *  defines
  */
 
-#define HP100_BUS_ISA		0
-#define HP100_BUS_EISA		1
-#define HP100_BUS_PCI		2
+#define HP100_BUS_ISA     0
+#define HP100_BUS_EISA    1
+#define HP100_BUS_PCI     2
+
+#ifndef PCI_DEVICE_ID_HP_J2585B
+#define PCI_DEVICE_ID_HP_J2585B 0x1031
+#endif
+#ifndef PCI_VENDOR_ID_COMPEX
+#define PCI_VENDOR_ID_COMPEX 0x11f6
+#endif
+#ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
+#endif
 
-#define HP100_REGION_SIZE	0x20
+#define HP100_REGION_SIZE  0x20 /* for ioports */
 
-#define HP100_MAX_PACKET_SIZE	(1536+4)
-#define HP100_MIN_PACKET_SIZE	60
+#define HP100_MAX_PACKET_SIZE  (1536+4)
+#define HP100_MIN_PACKET_SIZE  60
 
 #ifndef HP100_DEFAULT_RX_RATIO
-/* default - 65% onboard memory on the card are used for RX packets */
-#define HP100_DEFAULT_RX_RATIO	65
+/* default - 75% onboard memory on the card are used for RX packets */
+#define HP100_DEFAULT_RX_RATIO  75
 #endif
 
 #ifndef HP100_DEFAULT_PRIORITY_TX
@@ -140,193 +143,289 @@
 
 struct hp100_private {
   struct hp100_eisa_id *id;
+  u_short chip;
   u_short soft_model;
-  u_int memory_size;
-  u_short rx_ratio;		    /* 1 - 99 */
-  u_short priority_tx;	            /* != 0 - priority tx */
-  short mem_mapped;		    /* memory mapped access */
-  u_char *mem_ptr_virt;		    /* virtual memory mapped area, maybe NULL */
-  u_char *mem_ptr_phys;		    /* physical memory mapped area */
-  short lan_type;		    /* 10Mb/s, 100Mb/s or -1 (error) */
-  int hub_status;		    /* login to hub was successful? */
+  u_int memory_size; 
+  u_short rx_ratio;       /* 1 - 99 */
+  u_short priority_tx;    /* != 0 - priority tx */
+  u_short mode;           /* PIO, Shared Mem or Busmaster */
+  u_char bus;
+  u_char pci_bus;
+  u_char pci_device_fn;
+  short mem_mapped;	  /* memory mapped access */
+  u_int *mem_ptr_virt;    /* virtual memory mapped area, maybe NULL */
+  u_int *mem_ptr_phys;	  /* physical memory mapped area */
+  short lan_type;	  /* 10Mb/s, 100Mb/s or -1 (error) */
+  int hub_status;	  /* was login to hub successful? */
   u_char mac1_mode;
   u_char mac2_mode;
-  struct enet_statistics stats;
+  u_char hash_bytes[ 8 ];
+  hp100_stats_t stats;
+
+  /* Rings for busmaster mode: */
+  hp100_ring_t *rxrhead;  /* Head (oldest) index into rxring */
+  hp100_ring_t *rxrtail;  /* Tail (newest) index into rxring */
+  hp100_ring_t *txrhead;  /* Head (oldest) index into txring */
+  hp100_ring_t *txrtail;  /* Tail (newest) index into txring */
+
+  hp100_ring_t rxring[ MAX_RX_PDL ];
+  hp100_ring_t txring[ MAX_TX_PDL ];
+
+  u_int *page_vaddr;      /* Virtual address of allocated page */
+  u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */
+  int rxrcommit;          /* # Rx PDLs commited to adapter */
+  int txrcommit;          /* # Tx PDLs commited to adapter */
 };
 
 /*
  *  variables
  */
- 
+
 static struct hp100_eisa_id hp100_eisa_ids[] = {
 
-  /* 10/100 EISA card with REVA Cascade chip */
-  { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA }, 
+  /* 10/100 EISA card with revision A Cascade chip */
+  { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA },
 
-  /* 10/100 ISA card with REVA Cascade chip */
-  { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA },
+  /* 10/100 ISA card with revision A Cascade chip */
+  { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA },
 
   /* 10 only EISA card with Cascade chip */
-  { 0x02019F022, "HP 27248B",      HP100_BUS_EISA }, 
+  { 0x2019F022, "HP 27248B",      HP100_BUS_EISA },
 
   /* 10/100 EISA card with Cascade chip */
-  { 0x04019F022, "HP J2577",       HP100_BUS_EISA },
+  { 0x4019F022, "HP J2577",       HP100_BUS_EISA },
 
   /* 10/100 ISA card with Cascade chip */
-  { 0x05019F022, "HP J2573",       HP100_BUS_ISA },
+  { 0x5019F022, "HP J2573",       HP100_BUS_ISA },
+
+  /* 10/100 PCI card - old J2585A */
+  { 0x1030103c, "HP J2585A", 	    HP100_BUS_PCI },
+
+  /* 10/100 PCI card - new J2585B - master capable */
+  { 0x1041103c, "HP J2585B",      HP100_BUS_PCI },
+
+  /* 10 Mbit Combo Adapter */
+  { 0x1042103c, "HP J2970",       HP100_BUS_PCI },
 
-  /* 10/100 PCI card */
-  /* Note: ID for this card is same as PCI vendor/device numbers. */
-  { 0x01030103c, "HP J2585", 	   HP100_BUS_PCI },
+  /* 10 Mbit 10baseT Adapter */
+  { 0x1040103c, "HP J2973",       HP100_BUS_PCI },
+
+  /* 10/100 EISA card from Compex */
+  { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA },
+
+  /* 10/100 PCI card from Compex (J2585A compatible) */
+  { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI }
 };
 
-int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
-int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
+static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+static int hp100_mode = 1;
+
+#ifdef LINUX_2_1
+MODULE_PARM( hp100_rx_ratio, "1i" );
+MODULE_PARM( hp100_priority_tx, "1i" );
+MODULE_PARM( hp100_mode, "1i" );
+#endif
 
 /*
  *  prototypes
  */
 
-static int hp100_probe1( struct device *dev, int ioaddr, int bus );
-static int hp100_open( struct device *dev );
-static int hp100_close( struct device *dev );
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn  );
+static int  hp100_open( struct device *dev );
+static int  hp100_close( struct device *dev );
+static int  hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static int  hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev );
 static void hp100_rx( struct device *dev );
-static struct enet_statistics *hp100_get_stats( struct device *dev );
+static hp100_stats_t *hp100_get_stats( struct device *dev );
 static void hp100_update_stats( struct device *dev );
 static void hp100_clear_stats( int ioaddr );
 static void hp100_set_multicast_list( struct device *dev);
 static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs );
-
 static void hp100_start_interface( struct device *dev );
 static void hp100_stop_interface( struct device *dev );
 static void hp100_load_eeprom( struct device *dev );
-static int hp100_sense_lan( struct device *dev );
-static int hp100_login_to_vg_hub( struct device *dev );
-static int hp100_down_vg_link( struct device *dev );
+static int  hp100_sense_lan( struct device *dev );
+static int  hp100_login_to_vg_hub( struct device *dev, u_short force_relogin );
+static int  hp100_down_vg_link( struct device *dev );
+static void hp100_cascade_reset( struct device *dev, u_short enable );
+static void hp100_BM_shutdown( struct device *dev );
+static void hp100_mmuinit( struct device *dev );
+static void hp100_init_pdls( struct device *dev );
+static int  hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static int  hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static void hp100_rxfill( struct device *dev );
+static void hp100_hwinit( struct device *dev );
+static void hp100_clean_txring( struct device *dev );
+#ifdef HP100_DEBUG
+static void hp100_RegisterDump( struct device *dev );
+#endif
+
+/* TODO: This function should not really be needed in a good design... */
+static void wait( void )
+{
+  udelay( 1000 );
+}
 
 /*
  *  probe functions
+ *  These functions should - if possible - avoid doing write operations
+ *  since this could cause problems when the card is not installed.
  */
- 
-int hp100_probe( struct device *dev )
+
+__initfunc(int hp100_probe( struct device *dev ))
 {
   int base_addr = dev ? dev -> base_addr : 0;
-  int ioaddr;
+  int ioaddr = 0;
 #ifdef CONFIG_PCI
   int pci_start_index = 0;
 #endif
 
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4200, TRACE );
+  printk( "hp100: %s: probe\n", dev->name );
+#endif
+
   if ( base_addr > 0xff )	/* Check a single specified location. */
     {
       if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
       if ( base_addr < 0x400 )
-        return hp100_probe1( dev, base_addr, HP100_BUS_ISA );
-       else
-        return hp100_probe1( dev, base_addr, HP100_BUS_EISA );
+        return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
+      if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 )
+        return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
+#ifdef CONFIG_PCI
+      printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name );
+      return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 );
+#else
+      return -ENODEV;
+#endif
     }
-   else 
+  else
 #ifdef CONFIG_PCI
-  if ( base_addr > 0 && base_addr < 8 + 1 )
-    pci_start_index = 0x100 | ( base_addr - 1 );
-   else
+    if ( base_addr > 0 && base_addr < 8 + 1 )
+      pci_start_index = 0x100 | ( base_addr - 1 );
+    else
 #endif
-    if ( base_addr != 0 ) return -ENXIO;
+      if ( base_addr != 0 ) return -ENXIO;
 
   /* at first - scan PCI bus(es) */
-  
+
 #ifdef CONFIG_PCI
   if ( pcibios_present() )
     {
       int pci_index;
-      
+
 #ifdef HP100_DEBUG_PCI
-      printk( "hp100: PCI BIOS is present, checking for devices..\n" );
+      printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name );
 #endif
       for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
         {
           u_char pci_bus, pci_device_fn;
           u_short pci_command;
-          
-          if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
-          			    pci_index, &pci_bus,
-          			    &pci_device_fn ) != 0 ) break;
+
+          if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) && 
+              (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) &&
+              (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) ) break; 
+
           pcibios_read_config_dword( pci_bus, pci_device_fn,
-              		             PCI_BASE_ADDRESS_0, &ioaddr );
-              				 
-          ioaddr &= ~3;		/* remove I/O space marker in bit 0. */
-              
+                                     PCI_BASE_ADDRESS_0, &ioaddr );
+
+          ioaddr &= ~3;    /* remove I/O space marker in bit 0. */
+
           if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-              
+
           pcibios_read_config_word( pci_bus, pci_device_fn,
-              			    PCI_COMMAND, &pci_command );
+                                    PCI_COMMAND, &pci_command );
           if ( !( pci_command & PCI_COMMAND_MASTER ) )
             {
-#ifdef HP100_DEBUG_PCI
-              printk( "hp100: PCI Master Bit has not been set. Setting...\n" );
+#ifdef HP100_DEBUG
+              printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name );
 #endif
               pci_command |= PCI_COMMAND_MASTER;
               pcibios_write_config_word( pci_bus, pci_device_fn,
-                  			 PCI_COMMAND, pci_command );
+                                         PCI_COMMAND, pci_command );
             }
-#ifdef HP100_DEBUG_PCI
-          printk( "hp100: PCI adapter found at 0x%x\n", ioaddr );
+#ifdef HP100_DEBUG
+          printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr );
 #endif
-       	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0;
+	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
+	    return 0;
         }
     }
   if ( pci_start_index > 0 ) return -ENODEV;
 #endif /* CONFIG_PCI */
-         
-  /* at second - probe all EISA possible port regions (if EISA bus present) */
-  
+
+  /* Second: Probe all EISA possible port regions (if EISA bus present) */
   for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
     }
-         
-  /* at third - probe all ISA possible port regions */
-         
+
+  /* Third Probe all ISA possible port regions */
   for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
     }
-                                                                            
+
   return -ENODEV;
 }
 
-static int hp100_probe1( struct device *dev, int ioaddr, int bus )
+
+__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ))
 {
   int i;
+
   u_char uc, uc_1;
   u_int eisa_id;
+  u_int chip;
+  u_int memory_size = 0;
+  u_short local_mode, lsw;
   short mem_mapped;
-  u_char *mem_ptr_phys, *mem_ptr_virt;
+  u_int *mem_ptr_phys, *mem_ptr_virt;
   struct hp100_private *lp;
   struct hp100_eisa_id *eid;
 
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4201, TRACE );
+  printk("hp100: %s: probe1\n",dev->name);
+#endif
+
   if ( dev == NULL )
     {
 #ifdef HP100_DEBUG
-      printk( "hp100_probe1: dev == NULL ?\n" );
+      printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name );
 #endif
       return EIO;
     }
+  
+  if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) 
+    {
+      return -ENODEV;
+    }
+  else
+    {
+      chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK;
+#ifdef HP100_DEBUG
+      if ( chip == HP100_CHIPID_SHASTA )
+        printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_RAINIER )
+        printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_LASSEN )
+        printk("hp100: %s: Lassen Chip detected.\n", dev->name);
+      else
+        printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip);
+#endif 
+    }
 
-  if ( bus != HP100_BUS_PCI )		/* don't check PCI cards again */
-    if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 ||
-         inb( ioaddr + 1 ) != HP100_HW_ID_1 ||
-         ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA ||
-         inb( ioaddr + 3 ) != HP100_HW_ID_3 ) 
-       return -ENODEV;
-
-  dev -> base_addr = ioaddr;
-
-#ifdef HP100_DEBUG_PROBE1
-  printk( "hp100_probe1: card found at port 0x%x\n", ioaddr );
-#endif
+  dev->base_addr = ioaddr;
 
   hp100_page( ID_MAC_ADDR );
   for ( i = uc = eisa_id = 0; i < 4; i++ )
@@ -338,29 +437,25 @@
     }
   uc += hp100_inb( BOARD_ID + 4 );
 
-#ifdef HP100_DEBUG_PROBE1
-  printk( "hp100_probe1: EISA ID = 0x%08x  checksum = 0x%02x\n", eisa_id, uc );
-#endif
-
-  if ( uc != 0xff )		/* bad checksum? */
+  if ( uc != 0xff )    /* bad checksum? */
     {
-      printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr );
+      printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr );
       return -ENODEV;
-    }  
+    }
 
-  for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ )
+  for ( i=0; i<sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id); i++)
     if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
       break;
   if ( i >= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) )
     {
-      printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id );
+      printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id );
       return -ENODEV;
     }
   eid = &hp100_eisa_ids[ i ];
-  if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
+  if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
     {
-      printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", 
-	eid -> name, ioaddr );
+      printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n",
+	      dev->name, eid->name, ioaddr );
       return -ENODEV;
     }
 
@@ -368,469 +463,1607 @@
     uc += hp100_inb( LAN_ADDR + i );
   if ( uc != 0xff )
     {
-      printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", 
-	eid -> name, ioaddr );
+      printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n",
+	     dev->name, eid->name, ioaddr );
       return -EIO;
     }
 
-#ifndef HP100_IO_MAPPED
+  /* Determine driver operation mode
+   *
+   * Use the variable "hp100_mode" upon insmod or as kernel parameter to
+   * force driver modes:
+   * hp100_mode=1 -> default, use busmaster mode if configured.
+   * hp100_mode=2 -> enable shared memory mode 
+   * hp100_mode=3 -> force use of i/o mapped mode.
+   * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
+   */
+
+  /* hp100_mode value maybe used in future by another card */
+  local_mode=hp100_mode;
+  if ( local_mode < 1 || local_mode > 4 )
+    local_mode = 1;		/* default */
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  if(local_mode==3)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: IO mapped mode forced.\n", dev->name);
+    }
+  else if(local_mode==2)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: Shared memory mode requested.\n", dev->name);
+    } 
+  else if(local_mode==4)
+    {
+      if(chip==HP100_CHIPID_LASSEN)
+	{
+	  hp100_outw(HP100_BM_WRITE|
+		     HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
+	  hp100_outw(HP100_IO_EN   | 
+		     HP100_MEM_EN  | HP100_RESET_LB, OPTION_LSW);
+	  printk("hp100: %s: Busmaster mode requested.\n",dev->name);
+	}
+      local_mode=1;
+    }
+		
+  if(local_mode==1) /* default behaviour */
+    {
+      lsw = hp100_inw(OPTION_LSW);
+    
+      if ( (lsw & HP100_IO_EN) &&
+	   (~lsw & HP100_MEM_EN) &&
+	   (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) )
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: IO_EN bit is set on card.\n",dev->name);
+#endif
+	  local_mode=3;
+	}
+      else if ( chip == HP100_CHIPID_LASSEN &&
+	        ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) ==
+	                (HP100_BM_WRITE|HP100_BM_READ) )
+	{
+	  printk("hp100: %s: Busmaster mode enabled.\n",dev->name);
+	  hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW);
+	}
+      else
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n", dev->name);
+#endif
+	  /* In this case, try shared memory mode */
+	  local_mode=2;
+	  hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+	  /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
+	}
+    }
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  /* Check for shared memory on the card, eventually remap it */
   hp100_page( HW_MAP );
-  mem_mapped = ( hp100_inw( OPTION_LSW ) & 
-                 ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0;
+  mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0);
   mem_ptr_phys = mem_ptr_virt = NULL;
-  if ( mem_mapped )
+  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07));
+
+  /* For memory mapped or busmaster mode, we want the memory address */
+  if ( mem_mapped || (local_mode==1))
     {
-      mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) | 
-                               ( hp100_inw( MEM_MAP_MSW ) << 16 ) );
-      (u_int)mem_ptr_phys &= ~0x1fff;	/* 8k alignment */
+      mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | 
+				( hp100_inw( MEM_MAP_MSW ) << 16 ) );
+      (u_int)mem_ptr_phys &= ~0x1fff;  /* 8k alignment */
+
       if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
         {
+	  printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name);
           mem_ptr_phys = NULL;
           mem_mapped = 0;
+	  local_mode=3; /* Use programmed i/o */
         }
-      if ( mem_mapped && bus == HP100_BUS_PCI )
-        {
-          if ( ( mem_ptr_virt = vremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL )
-            {
-              printk( "hp100: vremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys );
-              mem_ptr_phys = NULL;
-              mem_mapped = 0;
-            }
-        }
-    }
-#else
-  mem_mapped = 0;
-  mem_ptr_phys = mem_ptr_virt = NULL;
+						
+      /* We do not need access to shared memory in busmaster mode */
+      /* However in slave mode we need to remap high (>1GB) card memory  */
+      if(local_mode!=1) /* = not busmaster */
+	{
+	  if ( bus == HP100_BUS_PCI )
+	    {
+	      /* We try with smaller memory sizes, if ioremap fails */
+	      for(; memory_size>16383; memory_size=memory_size/2)
+		{
+		  if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL)
+		    {
+#ifdef HP100_DEBUG
+		      printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, memory_size, (u_long)mem_ptr_phys );
+#endif
+		    }	
+		  else
+		    {
+#ifdef HP100_DEBUG
+		      printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt);
 #endif
+		      break;
+		    }
+		}
+														
+	      if(mem_ptr_virt==NULL) /* all ioremap tries failed */
+		{
+		  printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name);
+		  local_mode=3;
+		  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) );
+		}
+	    }
+	}
+						
+    }
+
+  if(local_mode==3) /* io mapped forced */
+    {
+      mem_mapped = 0;
+      mem_ptr_phys = mem_ptr_virt = NULL;
+      printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name);
+    }
 
-  if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL )
+  /* Initialise the "private" data structure for this card. */
+  if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
     return -ENOMEM;
-  memset( dev -> priv, 0, sizeof( struct hp100_private ) );
+  memset( dev->priv, 0, sizeof(struct hp100_private) );
 
-  lp = (struct hp100_private *)dev -> priv;
-  lp -> id = eid;
-  lp -> mem_mapped = mem_mapped;
-  lp -> mem_ptr_phys = mem_ptr_phys;
-  lp -> mem_ptr_virt = mem_ptr_virt;
+  lp = (struct hp100_private *)dev->priv;
+  lp->id = eid;
+  lp->chip = chip;
+  lp->mode = local_mode;
+  lp->pci_bus = pci_bus;
+  lp->bus = bus;
+  lp->pci_device_fn = pci_device_fn;
+  lp->priority_tx = hp100_priority_tx;
+  lp->rx_ratio = hp100_rx_ratio;
+  lp->mem_ptr_phys = mem_ptr_phys;
+  lp->mem_ptr_virt = mem_ptr_virt;
   hp100_page( ID_MAC_ADDR );
-  lp -> soft_model = hp100_inb( SOFT_MODEL );
-  lp -> mac1_mode = HP100_MAC1MODE3;
-  lp -> mac2_mode = HP100_MAC2MODE3;
-  
-  dev -> base_addr = ioaddr;
+  lp->soft_model = hp100_inb( SOFT_MODEL );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
+
+  dev->base_addr = ioaddr;
+
+  lp->memory_size = memory_size;
+  lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */
+
+  /* memory region for programmed i/o */
+  request_region( dev->base_addr, HP100_REGION_SIZE, eid->name );
+
+  dev->open = hp100_open;
+  dev->stop = hp100_close;
+
+  if (lp->mode==1) /* busmaster */
+    dev->hard_start_xmit = hp100_start_xmit_bm;
+  else
+    dev->hard_start_xmit = hp100_start_xmit;
+
+  dev->get_stats = hp100_get_stats;
+  dev->set_multicast_list = &hp100_set_multicast_list;
+
+  /* Ask the card for which IRQ line it is configured */
   hp100_page( HW_MAP );
-  dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK;
-  if ( dev -> irq == 2 ) dev -> irq = 9;
-  lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 );
-  lp -> rx_ratio = hp100_rx_ratio;
-
-  dev -> open = hp100_open;
-  dev -> stop = hp100_close;
-  dev -> hard_start_xmit = hp100_start_xmit;
-  dev -> get_stats = hp100_get_stats;
-  dev -> set_multicast_list = &hp100_set_multicast_list;
+  dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
+  if ( dev->irq == 2 )
+    dev->irq = 9;
 
-  request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name );
+  if(lp->mode==1) /* busmaster */
+    dev->dma=4; 
 
+  /* Ask the card for its MAC address and store it for later use. */
   hp100_page( ID_MAC_ADDR );
   for ( i = uc = 0; i < 6; i++ )
-    dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
+    dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
 
+  /* Reset statistics (counters) */
   hp100_clear_stats( ioaddr );
 
   ether_setup( dev );
 
-  lp -> lan_type = hp100_sense_lan( dev );
+  /* If busmaster mode is wanted, a dma-capable memory area is needed for
+   * the rx and tx PDLs 
+   * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
+   * needed for the allocation of the memory area. 
+   */
+
+  /* TODO: We do not need this with old cards, where PDLs are stored
+   * in the cards shared memory area. But currently, busmaster has been
+   * implemented/tested only with the lassen chip anyway... */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Get physically continous memory for TX & RX PDLs    */
+      if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL)
+        return -ENOMEM;
+      lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f));
+      memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f);
+
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n",
+      	     dev->name,
+             (u_int)lp->page_vaddr_algn,
+             (u_int)lp->page_vaddr_algn+MAX_RINGSIZE);
+#endif
+      lp->rxrcommit  = lp->txrcommit = 0;
+      lp->rxrhead    = lp->rxrtail   = &(lp->rxring[0]);
+      lp->txrhead    = lp->txrtail   = &(lp->txring[0]); 
+    }
+
+  /* Initialise the card. */
+  /* (I'm not really sure if it's a good idea to do this during probing, but 
+   * like this it's assured that the lan connection type can be sensed
+   * correctly)
+   */
+  hp100_hwinit( dev );
+
+  /* Try to find out which kind of LAN the card is connected to. */
+  lp->lan_type = hp100_sense_lan( dev );
      
-  printk( "%s: %s at 0x%x, IRQ %d, ",
-    dev -> name, lp -> id -> name, ioaddr, dev -> irq );
+  /* Print out a message what about what we think we have probed. */
+  printk( "hp100: %s: %s at 0x%x, IRQ %d, ",
+          dev->name, lp->id->name, ioaddr, dev->irq );
   switch ( bus ) {
-    case HP100_BUS_EISA: printk( "EISA" ); break;
-    case HP100_BUS_PCI:  printk( "PCI" );  break;
-    default:		 printk( "ISA" );  break;
+  case HP100_BUS_EISA: printk( "EISA" ); break;
+  case HP100_BUS_PCI:  printk( "PCI" );  break;
+  default:     printk( "ISA" );  break;
   }
   printk( " bus, %dk SRAM (rx/tx %d%%).\n",
-    lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio );
-  if ( mem_mapped )
+          lp->memory_size >> 10, lp->rx_ratio );
+
+  if ( lp->mode==2 ) /* memory mapped */ 
     {
-      printk( "%s: Memory area at 0x%lx-0x%lx",
-		dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff );
+      printk( "hp100: %s: Memory area at 0x%lx-0x%lx",
+              dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size );
       if ( mem_ptr_virt )
-        printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
+	printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
       printk( ".\n" );
+
+      /* Set for info when doing ifconfig */
+      dev->mem_start = (u_long)mem_ptr_phys;
+      dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size;
     }
-  printk( "%s: ", dev -> name );
-  if ( lp -> lan_type != HP100_LAN_ERR )
+  printk( "hp100: %s: ", dev->name );
+  if ( lp->lan_type != HP100_LAN_ERR )
     printk( "Adapter is attached to " );
-  switch ( lp -> lan_type ) {
-    case HP100_LAN_100:
-      printk( "100Mb/s Voice Grade AnyLAN network.\n" );
-      break;
-    case HP100_LAN_10:
-      printk( "10Mb/s network.\n" );
-      break;
-    default:
-      printk( "Warning! Link down.\n" );
+  switch ( lp->lan_type ) {
+  case HP100_LAN_100:
+    printk( "100Mb/s Voice Grade AnyLAN network.\n" );
+    break;
+  case HP100_LAN_10:
+    printk( "10Mb/s network.\n" );
+    break;
+  default:
+    printk( "Warning! Link down.\n" );
   }
+
+  return 0;
+}
+
+
+/* This procedure puts the card into a stable init state */
+static void hp100_hwinit( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4202, TRACE );
+  printk("hp100: %s: hwinit\n", dev->name);
+#endif
+
+  /* Initialise the card. -------------------------------------------- */
+
+  /* Clear all pending Ints and disable Ints */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* clear all pending ints */
+
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW );
+
+  if(lp->mode==1)
+    { 
+      hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */
+      wait();
+    }
+  else
+    {
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+      hp100_cascade_reset( dev, TRUE );
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); 
+    }
 		
-  hp100_stop_interface( dev );
+  /* Initiate EEPROM reload */
+  hp100_load_eeprom( dev );
+		
+  wait();
+		
+  /* Go into reset again. */
+  hp100_cascade_reset( dev, TRUE );
+		
+  /* Set Option Registers to a safe state  */
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    |
+              HP100_BM_WRITE |
+              HP100_BM_READ  | HP100_RESET_HB |
+              HP100_FAKE_INT |
+              HP100_INT_EN   |
+	      HP100_MEM_EN   |
+              HP100_IO_EN    | HP100_RESET_LB, OPTION_LSW);
+		
+  hp100_outw( HP100_TRI_INT  |
+              HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+
+  hp100_outb( HP100_PRIORITY_TX |
+              HP100_ADV_NXT_PKT |
+              HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+
+  /* TODO: Configure MMU for Ram Test. */
+  /* TODO: Ram Test. */
+
+  /* Re-check if adapter is still at same i/o location      */
+  /* (If the base i/o in eeprom has been changed but the    */
+  /* registers had not been changed, a reload of the eeprom */
+  /* would move the adapter to the address stored in eeprom */
   
-  return 0;
+  /* TODO: Code to implement. */
+		
+  /* Until here it was code from HWdiscover procedure. */
+  /* Next comes code from mmuinit procedure of SCO BM driver which is
+   * called from HWconfigure in the SCO driver.  */
+
+  /* Initialise MMU, eventually switch on Busmaster Mode, initialise 
+   * multicast filter...
+   */
+  hp100_mmuinit( dev );
+
+  /* We don't turn the interrupts on here - this is done by start_interface. */
+  wait(); /* TODO: Do we really need this? */
+
+  /* Enable Hardware (e.g. unreset) */
+  hp100_cascade_reset( dev, FALSE );
+
+  /* ------- initialisation complete ----------- */
+
+  /* Finally try to log in the Hub if there may be a VG connection. */
+  if( lp->lan_type != HP100_LAN_10 )
+    hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
 }
 
-/*
- *  open/close functions
+
+/* 
+ * mmuinit - Reinitialise Cascade MMU and MAC settings.
+ * Note: Must already be in reset and leaves card in reset. 
  */
-
-static int hp100_open( struct device *dev )
+static void hp100_mmuinit( struct device *dev )
 {
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
   int i;
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
 
-  if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name, NULL ) )
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4203, TRACE );
+  printk("hp100: %s: mmuinit\n",dev->name);
+#endif
+
+#ifdef HP100_DEBUG
+  if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) )
     {
-      printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq );
-      return -EAGAIN;
+      printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name);
+      return;
     }
-  irq2dev_map[ dev -> irq ] = dev;
+#endif
 
-  MOD_INC_USE_COUNT;
-  
-  dev -> tbusy = 0;
-  dev -> trans_start = jiffies;
-  dev -> interrupt = 0;
-  dev -> start = 1;
-
-  lp -> lan_type = hp100_sense_lan( dev );
-  lp -> mac1_mode = HP100_MAC1MODE3;
-  lp -> mac2_mode = HP100_MAC2MODE3;
+  /* Make sure IRQs are masked off and ack'ed. */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /*
+   * Enable Hardware 
+   * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
+   * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
+   * - Clear Priority, Advance Pkt and Xmit Cmd
+   */
+
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    | HP100_RESET_HB |
+              HP100_IO_EN    |
+              HP100_FAKE_INT |
+              HP100_INT_EN   | HP100_RESET_LB, OPTION_LSW );
+  	
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
+		
+  if(lp->mode==1) /* busmaster */
+    {
+      hp100_outw( HP100_BM_WRITE | 
+		  HP100_BM_READ  |
+		  HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+    }
+  else if(lp->mode==2) /* memory mapped */
+    {
+      hp100_outw( HP100_BM_WRITE |
+		  HP100_BM_READ  | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW );
+      hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
+    }
+  else if( lp->mode==3 ) /* i/o mapped mode */
+    {
+      hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | 
+                  HP100_IO_EN    | HP100_SET_LB, OPTION_LSW );
+    }
+
+  hp100_page( HW_MAP );
+  hp100_outb( 0, EARLYRXCFG );
+  hp100_outw( 0, EARLYTXCFG );
   
-  hp100_page( MAC_CTRL );
-  hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 );
+  /*
+   * Enable Bus Master mode
+   */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Experimental: Set some PCI configuration bits */
+      hp100_page( HW_MAP );
+      hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */
+      hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */
+
+      /* PCI Bus failures should result in a Misc. Interrupt */
+      hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2);
+					
+      hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW );
+      hp100_page( HW_MAP );					
+      /* Use Burst Mode and switch on PAGE_CK */
+      hp100_orb( HP100_BM_BURST_RD |
+                 HP100_BM_BURST_WR, BM);
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+	hp100_orb( HP100_BM_PAGE_CK, BM );
+      hp100_orb( HP100_BM_MASTER, BM );		
+    }
+  else /* not busmaster */
+    {
+      hp100_page(HW_MAP);
+      hp100_andb(~HP100_BM_MASTER, BM );
+    }
+
+  /*
+   * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
+   */  
+  hp100_page( MMU_CFG );
+  if(lp->mode==1) /* only needed for Busmaster */
+    {
+      int xmit_stop, recv_stop;
 
-  hp100_stop_interface( dev );
-  hp100_load_eeprom( dev );
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+        {
+          int pdl_stop;
+          
+          /*
+           * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
+           * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
+           * to the next higher 1k boundary) bytes for the rx-pdl's
+	   * Note: For non-etr chips the transmit stop register must be
+	   * programmed on a 1k boundary, i.e. bits 9:0 must be zero. 
+	   */
+          pdl_stop  = lp->memory_size;
+          xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff);
+          recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff);
+          hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP );
+#ifdef HP100_DEBUG_BM
+          printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop);
+#endif
+        }
+      else /* ETR chip (Lassen) in busmaster mode */
+        {
+          xmit_stop = ( lp->memory_size ) - 1;
+          recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff);
+        }
+
+      hp100_outw( xmit_stop>>4 , TX_MEM_STOP );
+      hp100_outw( recv_stop>>4 , RX_MEM_STOP );
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: TX_STOP  = 0x%x\n",dev->name,xmit_stop>>4);
+      printk("hp100: %s: RX_STOP  = 0x%x\n",dev->name,recv_stop>>4);
+#endif
+    }  
+  else /* Slave modes (memory mapped and programmed io)  */
+    {
+      hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP );
+      hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP );  
+#ifdef HP100_DEBUG
+      printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP));
+      printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP));
+#endif
+    }
 
-  hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | 
-              HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
-  hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB |
-              HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW );
-  hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB |
-                HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ),
-              OPTION_MSW );
-              				
+  /* Write MAC address into page 1 */
   hp100_page( MAC_ADDRESS );
   for ( i = 0; i < 6; i++ )
-    hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i );
-  for ( i = 0; i < 8; i++ )		/* setup multicast filter to receive all */
-    hp100_outb( 0xff, HASH_BYTE0 + i );
+    hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i );
+  
+  /* Zero the multicast hash registers */
+  for ( i = 0; i < 8; i++ )
+    hp100_outb( 0x0, HASH_BYTE0 + i );  
+  
+  /* Set up MAC defaults */
+  hp100_page( MAC_CTRL );
+ 
+  /* Go to LAN Page and zero all filter bits */
+  /* Zero accept error, accept multicast, accept broadcast and accept */
+  /* all directed packet bits */
+  hp100_andb( ~(HP100_RX_EN|
+		HP100_TX_EN|
+		HP100_ACC_ERRORED|
+		HP100_ACC_MC|
+		HP100_ACC_BC|
+		HP100_ACC_PHY),   MAC_CFG_1 );
+
+  hp100_outb( 0x00, MAC_CFG_2 );
+
+  /* Zero the frame format bit. This works around a training bug in the */
+  /* new hubs. */
+  hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */	
+
+  if(lp->priority_tx)
+    hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW );
+  else
+    hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW );
+	
+  hp100_outb( HP100_ADV_NXT_PKT |
+	      HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+	
+  /* If busmaster, initialize the PDLs */
+  if(lp->mode==1)
+    hp100_init_pdls( dev );
+
+  /* Go to performance page and initalize isr and imr registers */
   hp100_page( PERFORMANCE );
-  hp100_outw( 0xfefe, IRQ_MASK );	/* mask off all ints */
-  hp100_outw( 0xffff, IRQ_STATUS );	/* ack IRQ */
-  hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) |
-              (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK );
-              				/* and enable few */
-  hp100_reset_card();
-  hp100_page( MMU_CFG );
-  hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP );
-  hp100_outw( lp -> memory_size - 1, TX_MEM_STOP );
-  hp100_unreset_card();
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+}
+
+
+/*
+ *  open/close functions
+ */
+
+static int hp100_open( struct device *dev )
+{
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+#ifdef HP100_DEBUG_B
+  int ioaddr=dev->base_addr;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4204, TRACE );
+  printk("hp100: %s: open\n",dev->name);
+#endif
+		
+  /* New: if bus is PCI or EISA, interrupts might be shared interrupts */
+  if ( request_irq(dev->irq, hp100_interrupt,
+  		   lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT,
+  		   lp->id->name, dev))
+    {
+      printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq );
+      return -EAGAIN;
+    }
 
-  if ( lp -> lan_type == HP100_LAN_100 )
-    lp -> hub_status = hp100_login_to_vg_hub( dev );
+  MOD_INC_USE_COUNT;
+
+  dev->tbusy = 0;
+  dev->trans_start = jiffies;
+  dev->interrupt = 0;
+  dev->start = 1;
+
+  lp->lan_type = hp100_sense_lan( dev );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
+
+  hp100_stop_interface( dev );
+ 
+  hp100_hwinit( dev );
 
-  hp100_start_interface( dev );
+  hp100_start_interface( dev ); /* sets mac modes, enables interrupts */
 
   return 0;
 }
 
+
+/* The close function is called when the interface is to be brought down */
 static int hp100_close( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4205, TRACE );
+  printk("hp100: %s: close\n", dev->name);
+#endif
 
   hp100_page( PERFORMANCE );
-  hp100_outw( 0xfefe, IRQ_MASK );		/* mask off all IRQs */
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all IRQs */
 
   hp100_stop_interface( dev );
 
-  if ( lp -> lan_type == HP100_LAN_100 )	/* relogin */
-    hp100_login_to_vg_hub( dev );
+  if ( lp->lan_type == HP100_LAN_100 ) 
+    lp->hub_status=hp100_login_to_vg_hub( dev, FALSE );
+
+  dev->tbusy = 1;
+  dev->start = 0;
 
-  dev -> tbusy = 1;
-  dev -> start = 0;
+  free_irq( dev->irq, dev );
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
 
-  free_irq( dev -> irq, NULL );
-  irq2dev_map[ dev -> irq ] = NULL;
   MOD_DEC_USE_COUNT;
   return 0;
 }
 
-/* 
- *  transmit
+
+/*
+ * Configure the PDL Rx rings and LAN 
  */
-
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+static void hp100_init_pdls( struct device *dev )
 {
-  int i, ok_flag;
-  int ioaddr = dev -> base_addr;
-  u_short val;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
-
-  if ( lp -> lan_type < 0 )
-    {
-      hp100_stop_interface( dev );
-      if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 )
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t         *ringptr;
+  u_int                *pageptr;
+  int                  i;
+
+#ifdef HP100_DEBUG_B
+  int ioaddr = dev->base_addr;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4206, TRACE );
+  printk("hp100: %s: init pdls\n", dev->name);
+#endif
+
+  if(0==lp->page_vaddr_algn)
+    printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name);
+  else
+    {
+      /* pageptr shall point into the DMA accessible memory region  */
+      /* we use this pointer to status the upper limit of allocated */
+      /* memory in the allocated page. */
+      /* note: align the pointers to the pci cache line size */
+      memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero  Rx/Tx ring page */
+      pageptr=lp->page_vaddr_algn;
+						
+      lp->rxrcommit =0;
+      ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]);
+      
+      /* Initialise Rx Ring */
+      for (i=MAX_RX_PDL-1; i>=0; i--)
         {
-          printk( "%s: no connection found - check wire\n", dev -> name );
-          hp100_start_interface( dev );	/* 10Mb/s RX packets maybe handled */
-          return -EIO;
+          lp->rxring[i].next = ringptr;
+          ringptr=&(lp->rxring[i]);
+          pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr);
         }
-      if ( lp -> lan_type == HP100_LAN_100 )
-        lp -> hub_status = hp100_login_to_vg_hub( dev );
-      hp100_start_interface( dev );
-    }
-  
-  if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 )
-    {
-#ifdef HP100_DEBUG
-      printk( "hp100_start_xmit: rx free mem = 0x%x\n", i );
-#endif
-      if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN;
-      if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 )
- 				/* 100Mb/s adapter isn't connected to hub */
+      
+      /* Initialise Tx Ring */
+      lp->txrcommit = 0;
+      ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
+      for (i=MAX_TX_PDL-1; i>=0; i--)
         {
-          printk( "%s: login to 100Mb/s hub retry\n", dev -> name );
-          hp100_stop_interface( dev );
-          lp -> hub_status = hp100_login_to_vg_hub( dev );
-          hp100_start_interface( dev );
+          lp->txring[i].next = ringptr;
+          ringptr=&(lp->txring[i]);
+          pageptr+=hp100_init_txpdl(dev, ringptr, pageptr);
         }
-       else
+    }
+}
+
+
+/* These functions "format" the entries in the pdl structure   */
+/* They return how much memory the fragments need.            */
+static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
+{
+  /* pdlptr is starting adress for this pdl */
+
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr);
+
+  ringptr->pdl       = pdlptr+1; 
+  ringptr->pdl_paddr = virt_to_bus(pdlptr+1);
+  ringptr->skb       = (void *) NULL; 
+
+  /* 
+   * Write address and length of first PDL Fragment (which is used for
+   * storing the RX-Header
+   * We use the 4 bytes _before_ the PDH in the pdl memory area to 
+   * store this information. (PDH is at offset 0x04)
+   */
+  /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
+
+  *(pdlptr+2) =(u_int) virt_to_bus(pdlptr);  /* Address Frag 1 */ 
+  *(pdlptr+3) = 4;                           /* Length  Frag 1 */
+
+  return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 );
+}
+
+
+static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
+{
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr);
+
+  ringptr->pdl       = pdlptr; /* +1; */  
+  ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */
+  ringptr->skb = (void *) NULL;
+  
+  return((((MAX_TX_FRAG*2+2)+3)/4)*4);
+}
+
+
+/*
+ * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes 
+ * for possible odd word alignment rounding up to next dword and set PDL
+ * address for fragment#2 
+ * Returns: 0 if unable to allocate skb_buff
+ *          1 if successful
+ */
+int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev )
+{
+#ifdef HP100_DEBUG_B
+  int ioaddr = dev->base_addr;
+#endif
+#ifdef HP100_DEBUG_BM
+  u_int *p;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4207, TRACE );
+  printk("hp100: %s: build rx pdl\n", dev->name);
+#endif
+
+  /* Allocate skb buffer of maximum size */
+  /* Note: This depends on the alloc_skb functions allocating more 
+   * space than requested, i.e. aligning to 16bytes */
+
+  ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 );
+		
+  if(NULL!=ringptr->skb)
+    {
+      /* 
+       * Reserve 2 bytes at the head of the buffer to land the IP header
+       * on a long word boundary (According to the Network Driver section
+       * in the Linux KHG, this should help to increase performance.)
+       */
+      skb_reserve(ringptr->skb, 2);
+
+      ringptr->skb->dev=dev; 
+      ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE );
+						
+      /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
+      /* Note: 1st Fragment is used for the 4 byte packet status
+       * (receive header). Its PDL entries are set up by init_rxpdl. So 
+       * here we only have to set up the PDL fragment entries for the data
+       * part. Those 4 bytes will be stored in the DMA memory region 
+       * directly before the PDL. 
+       */
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
+      	     dev->name,
+	     (u_int) ringptr->pdl,
+	     ((MAX_ETHER_SIZE+2+3)/4)*4,
+	     (unsigned int) ringptr->skb->data);
+#endif
+
+      ringptr->pdl[0] = 0x00020000;                          /* Write PDH */
+      ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); 
+      ringptr->pdl[4] = MAX_ETHER_SIZE;                 /* Length of Data */
+						
+#ifdef HP100_DEBUG_BM
+      for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++)
+        printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p );
+#endif
+      return(1);
+    }
+  /* else: */
+  /* alloc_skb failed (no memory) -> still can receive the header
+   * fragment into PDL memory. make PDL safe by clearing msgptr and
+   * making the PDL only 1 fragment (i.e. the 4 byte packet status)
+   */
+#ifdef HP100_DEBUG_BM
+  printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n",
+  	 dev->name,
+	 (u_int) ringptr->pdl);
+#endif
+
+  ringptr->pdl[0]=0x00010000;   /* PDH: Count=1 Fragment */
+
+  return(0);
+}
+
+
+/*
+ *  hp100_rxfill - attempt to fill the Rx Ring will empty skb's
+ *
+ * Makes assumption that skb's are always contiguous memory areas and
+ * therefore PDLs contain only 2 physical fragments.
+ * -  While the number of Rx PDLs with buffers is less than maximum
+ *      a.  Get a maximum packet size skb
+ *      b.  Put the physical address of the buffer into the PDL.
+ *      c.  Output physical address of PDL to adapter.
+ */
+static void hp100_rxfill( struct device *dev )
+{
+  int ioaddr=dev->base_addr; 
+
+  struct hp100_private  *lp      = (struct hp100_private *)dev->priv;
+  hp100_ring_t    *ringptr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4208, TRACE );
+  printk("hp100: %s: rxfill\n",dev->name);
+#endif
+		
+  hp100_page( PERFORMANCE );
+
+  while (lp->rxrcommit < MAX_RX_PDL)
+    {
+      /*
+      ** Attempt to get a buffer and build a Rx PDL.
+      */
+      ringptr = lp->rxrtail;
+      if (0 == hp100_build_rx_pdl( ringptr, dev ))
         {
-          hp100_ints_off();
-          i = hp100_sense_lan( dev );
-          hp100_page( PERFORMANCE );
-          hp100_ints_on();
-          if ( i == HP100_LAN_ERR )
-            printk( "%s: link down detected\n", dev -> name );
-           else
-          if ( lp -> lan_type != i )
+          return;      /* None available, return */
+        }
+      
+      /* Hand this PDL over to the card */
+      /* Note: This needs performance page selected! */
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
+      	     dev->name,
+             lp->rxrcommit,
+             (u_int)ringptr->pdl,
+             (u_int)ringptr->pdl_paddr,
+             (u_int)ringptr->pdl[3]);
+#endif
+
+      hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); 
+      
+      lp->rxrcommit += 1;
+      lp->rxrtail = ringptr->next;
+    }
+}
+
+
+/*
+ * BM_shutdown - shutdown bus mastering and leave chip in reset state
+ */
+
+static void hp100_BM_shutdown( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  unsigned long time;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4209, TRACE );
+  printk("hp100: %s: bm shutdown\n",dev->name);
+#endif
+
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */
+
+  /* Ensure Interrupts are off */
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW );
+
+  /* Disable all MAC activity */
+  hp100_page( MAC_CTRL );
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
+
+  /* If cascade MMU is not already in reset */
+  if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) )
+    {
+      /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
+       * MMU pointers will not be reset out from underneath
+       */
+      hp100_page( MAC_CTRL );
+      for(time=0; time<5000; time++)
+        {
+          if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))==
+              (HP100_TX_IDLE|HP100_RX_IDLE) ) break;
+        }
+      
+      /* Shutdown algorithm depends on the generation of Cascade */
+      if( lp->chip==HP100_CHIPID_LASSEN )
+        { /* ETR shutdown/reset */
+          /* Disable Busmaster mode and wait for bit to go to zero. */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+          /* 100 ms timeout */
+          for(time=0; time<32000; time++)
             {
-              /* it's very heavy - all network setting must be changed!!! */
-              printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name );
-              lp -> lan_type = i;
-              hp100_stop_interface( dev );
-              if ( lp -> lan_type == HP100_LAN_100 )
-                lp -> hub_status = hp100_login_to_vg_hub( dev );
-              hp100_start_interface( dev );
+              if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break;
             }
-           else
+        }
+      else
+        { /* Shasta or Rainier Shutdown/Reset */
+          /* To ensure all bus master inloading activity has ceased,
+           * wait for no Rx PDAs or no Rx packets on card. 
+           */
+          hp100_page( PERFORMANCE );
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++)
             {
-              printk( "%s: interface reset\n", dev -> name );
-              hp100_stop_interface( dev );
-              hp100_start_interface( dev );
+              /* RX_PDL: PDLs not executed. */
+              /* RX_PKT_CNT: RX'd packets on card. */
+              if ( (hp100_inb( RX_PDL ) == 0) &&
+                   (hp100_inb( RX_PKT_CNT ) == 0) ) break;
             }
-        }
-      dev -> trans_start = jiffies;
-      return -EAGAIN;
+          
+          if(time>=10000)
+            printk("hp100: %s: BM shutdown error.\n", dev->name);
+          
+          /* To ensure all bus master outloading activity has ceased,
+           * wait until the Tx PDA count goes to zero or no more Tx space
+           * available in the Tx region of the card. 
+           */
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++) {
+            if ( (0 == hp100_inb( TX_PKT_CNT )) &&
+                 (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break;
+          } 
+          
+          /* Disable Busmaster mode */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+        } /* end of shutdown procedure for non-etr parts */  
+						
+      hp100_cascade_reset( dev, TRUE );
     }
-    
-  if ( skb == NULL )
+  hp100_page( PERFORMANCE );
+  /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */
+  /* Busmaster mode should be shut down now. */
+}
+
+
+
+/* 
+ *  transmit functions
+ */
+
+/* tx function for busmaster mode */
+static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev )
+{
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ringptr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4210, TRACE );
+  printk("hp100: %s: start_xmit_bm\n",dev->name);
+#endif
+
+  if ( skb==NULL )
     {
       dev_tint( dev );
       return 0;
     }
-    
-  if ( skb -> len <= 0 ) return 0;
+	
+  if ( skb->len <= 0 ) return 0;
+	
+  /* Get Tx ring tail pointer */
+  if( lp->txrtail->next==lp->txrhead )
+    {
+      /* No memory. */
+#ifdef HP100_DEBUG
+      printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name);
+#endif
+      /* not waited long enough since last tx? */
+      if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN;
+
+      if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+	{
+	  hp100_stop_interface( dev );
+	  if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+	    {
+	      printk( "hp100: %s: no connection found - check wire\n", dev->name );
+	      hp100_start_interface( dev );  /* 10Mb/s RX pkts maybe handled */
+	      return -EIO;
+	    }
+	  if ( lp->lan_type == HP100_LAN_100 )
+	    lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+	  hp100_start_interface( dev );
+	}
+				
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
+	{
+	  printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+	  hp100_stop_interface( dev );
+	  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+	  hp100_start_interface( dev );
+	}
+      else
+	{
+	  hp100_ints_off();
+	  i = hp100_sense_lan( dev );
+	  hp100_ints_on();
+	  if ( i == HP100_LAN_ERR )
+	    printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		hp100_start_interface( dev );
+	      }
+	}
+
+      dev->trans_start = jiffies;
+      return -EAGAIN;
+    }
+	
+  /*
+   * we have to turn int's off before modifying this, otherwise
+   * a tx_pdl_cleanup could occur at the same time
+   */
+  cli();
+  ringptr=lp->txrtail;
+  lp->txrtail=ringptr->next;
+	
+  /* Check whether packet has minimal packet size */
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+					
+  ringptr->skb=skb;
+  ringptr->pdl[0]=((1<<16) | i);                /* PDH: 1 Fragment & length */
+  ringptr->pdl[1]=(u32)virt_to_bus(skb->data);  /* 1st Frag: Adr. of data */
+  if(lp->chip==HP100_CHIPID_SHASTA)
+    {
+      /* TODO:Could someone who has the EISA card please check if this works? */
+      ringptr->pdl[2]=i;
+    }
+  else /* Lassen */
+    {
+      /* In the PDL, don't use the padded size but the real packet size: */
+      ringptr->pdl[2]=skb->len;              /* 1st Frag: Length of frag */
+    }
+
+  /* Hand this PDL to the card. */
+  hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */
+	
+  lp->txrcommit++;
+  sti();
+	
+  /* Update statistics */	
+  lp->stats.tx_packets++;
+#ifdef LINUX_2_1
+  lp->stats.tx_bytes += skb->len;
+#endif
+  dev->trans_start = jiffies;
+	
+  return 0;
+}
+
+
+/* clean_txring checks if packets have been sent by the card by reading
+ * the TX_PDL register from the performance page and comparing it to the
+ * number of commited packets. It then frees the skb's of the packets that
+ * obviously have been sent to the network.
+ *
+ * Needs the PERFORMANCE page selected. 
+ */
+static void hp100_clean_txring( struct device *dev )
+{
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int    ioaddr = dev->base_addr;
+  int    donecount;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4211, TRACE );
+  printk("hp100: %s: clean txring\n", dev->name);
+#endif
 
-  for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+  /* How many PDLs have been transmitted? */
+  donecount=(lp->txrcommit)-hp100_inb(TX_PDL);
+
+#ifdef HP100_DEBUG
+  if(donecount>MAX_TX_PDL)
+    printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name);
+#endif
+
+  for( ; 0!=donecount; donecount-- )
     {
-#ifdef HP100_DEBUG_TX
-      printk( "hp100_start_xmit: busy\n" );
-#endif    
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
+             dev->name,
+	     (u_int) lp->txrhead->skb->data,
+	     lp->txrcommit,
+	     hp100_inb(TX_PDL),
+	     donecount);
+#endif
+      dev_kfree_skb( lp->txrhead->skb, FREE_WRITE );
+      lp->txrhead->skb=(void *)NULL;
+      lp->txrhead=lp->txrhead->next;
+      lp->txrcommit--;
     }
+}
 
-  hp100_ints_off();
-  val = hp100_inw( IRQ_STATUS );
-  hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS );
-#ifdef HP100_DEBUG_TX
-  printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len );
+
+/* tx function for slave modes */
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+{
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  u_short val;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4212, TRACE );
+  printk("hp100: %s: start_xmit\n", dev->name);
 #endif
-  ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE;
-  i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE;
-  hp100_outw( i, DATA32 );		/* length to memory manager */
-  hp100_outw( i, FRAGMENT_LEN );
-  if ( lp -> mem_mapped )
-    {
-      if ( lp -> mem_ptr_virt )
-        {
-          memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len );
-          if ( !ok_flag )
-            memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+
+  if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+    {
+      hp100_stop_interface( dev );
+      if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+        {
+          printk( "hp100: %s: no connection found - check wire\n", dev->name );
+          hp100_start_interface( dev );  /* 10Mb/s RX packets maybe handled */
+          return -EIO;
         }
-       else
+      if ( lp->lan_type == HP100_LAN_100 )
+        lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+      hp100_start_interface( dev );
+    }
+
+  /* If there is not enough free memory on the card... */
+  i=hp100_inl(TX_MEM_FREE)&0x7fffffff;
+  if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) )
+    {
+#ifdef HP100_DEBUG
+      printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i );
+#endif
+      /* not waited long enough since last failed tx try? */
+      if ( jiffies - dev->trans_start < HZ/2 ) 
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: trans_start timing problem\n", dev->name);
+#endif
+	  return -EAGAIN;
+	}
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
         {
-          memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len );
-          if ( !ok_flag )
-            memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+          printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+          hp100_stop_interface( dev );
+          lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+          hp100_start_interface( dev );
         }
+      else
+        {
+          hp100_ints_off();
+          i = hp100_sense_lan( dev );
+          hp100_ints_on();
+          if ( i == HP100_LAN_ERR )
+            printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		hp100_start_interface( dev );
+		udelay(1000);
+	      }
+        }
+      dev->trans_start = jiffies;
+      return -EAGAIN;
     }
-   else
+
+  for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+    {
+#ifdef HP100_DEBUG_TX
+      printk( "hp100: %s: start_xmit: busy\n", dev->name );
+#endif
+    }
+	
+  hp100_ints_off();
+  val = hp100_inw( IRQ_STATUS );
+  /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
+   * when the current packet being transmitted on the wire is completed. */
+  hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); 
+#ifdef HP100_DEBUG_TX
+  printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len );
+#endif
+
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+
+  hp100_outw( i, DATA32 );       /* tell card the total packet length */
+  hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length    */
+	
+  if ( lp->mode==2 ) /* memory mapped */
+    {
+      if ( lp->mem_ptr_virt ) /* high pci memory was remapped */
+	{
+	  /* Note: The J2585B needs alignment to 32bits here!  */
+	  memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 );
+	  if ( !ok_flag )
+	    memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+      else
+	{
+	  memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len );
+	  if ( !ok_flag )
+	    memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+    }
+  else /* programmed i/o */
     {
-      outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 );
+      outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 );
       if ( !ok_flag )
-        for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
-          hp100_outl( 0, DATA32 );
+	for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
+	  hp100_outl( 0, DATA32 );
     }
-  hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
-  lp -> stats.tx_packets++;
-  dev -> trans_start = jiffies;
+	
+  hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
+	
+  lp->stats.tx_packets++;
+#ifdef LINUX_2_1
+  lp->stats.tx_bytes += skb->len;
+#endif
+  dev->trans_start=jiffies;
   hp100_ints_on();
-
+	
   dev_kfree_skb( skb, FREE_WRITE );
-
+	
 #ifdef HP100_DEBUG_TX
-  printk( "hp100_start_xmit: end\n" );
+  printk( "hp100: %s: start_xmit: end\n", dev->name );
 #endif
-
+	
   return 0;
 }
 
+
 /*
- *  receive - called from interrupt handler
+ * Receive Function (Non-Busmaster mode)
+ * Called when an "Receive Packet" interrupt occurs, i.e. the receive 
+ * packet counter is non-zero.
+ * For non-busmaster, this function does the whole work of transfering
+ * the packet to the host memory and then up to higher layers via skb
+ * and netif_rx. 
  */
 
 static void hp100_rx( struct device *dev )
 {
   int packets, pkt_len;
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
   u_int header;
   struct sk_buff *skb;
 
-#if 0
-  if ( lp -> lan_type < 0 )
-    {
-      if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 )
-        lp -> hub_status = hp100_login_to_vg_hub( dev );
-      hp100_page( PERFORMANCE );
-    }
+#ifdef DEBUG_B
+  hp100_outw( 0x4213, TRACE );
+  printk("hp100: %s: rx\n", dev->name);
 #endif
-  
+
+  /* First get indication of received lan packet */
+  /* RX_PKT_CND indicates the number of packets which have been fully */
+  /* received onto the card but have not been fully transfered of the card */
   packets = hp100_inb( RX_PKT_CNT );
-#ifdef HP100_DEBUG
+#ifdef HP100_DEBUG_RX
   if ( packets > 1 )
-    printk( "hp100_rx: waiting packets = %d\n", packets );
+    printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets );
 #endif
+
   while ( packets-- > 0 )
     {
-      for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ )
+      /* If ADV_NXT_PKT is still set, we have to wait until the card has */
+      /* really advanced to the next packet. */
+      for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT);
+	   pkt_len++ )
         {
-#ifdef HP100_DEBUG_TX
-          printk( "hp100_rx: busy, remaining packets = %d\n", packets );
+#ifdef HP100_DEBUG_RX
+          printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets );
 #endif    
         }
-      if ( lp -> mem_mapped )
+
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      if( lp->mode==2 ) /* memory mapped mode */
         {
-          if ( lp -> mem_ptr_virt )
-            header = *(__u32 *)lp -> mem_ptr_virt;
-           else
-            header = readl( lp -> mem_ptr_phys );
+          if ( lp->mem_ptr_virt )    /* if memory was remapped */
+            header = *(__u32 *)lp->mem_ptr_virt;
+          else
+            header = readl( lp->mem_ptr_phys );
         }
-       else
+      else /* programmed i/o */
         header = hp100_inl( DATA32 );
+      
       pkt_len = header & HP100_PKT_LEN_MASK;
+
 #ifdef HP100_DEBUG_RX
-      printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n",
-      	header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 );
+      printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8,
+              (header>>16)&7);
 #endif
-      /*
-       * NOTE! This (and the skb_put() below) depends on the skb-functions
+    
+      /* Now we allocate the skb and transfer the data into it. */  
+      /* NOTE! This (and the skb_put() below) depends on the skb-functions
        * allocating more than asked (notably, aligning the request up to
        * the next 16-byte length).
        */
       skb = dev_alloc_skb( pkt_len );
-      if ( skb == NULL )
-        {
+      if ( skb == NULL ) /* Not enough memory->drop packet */
+	{
 #ifdef HP100_DEBUG
-          printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len );
+	  printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len );
 #endif
-          lp -> stats.rx_dropped++;
-        }
-       else
-        {
-          u_char *ptr;
-        
-          skb -> dev = dev;
-          ptr = (u_char *)skb_put( skb, pkt_len );
-          if ( lp -> mem_mapped )
+	  lp->stats.rx_dropped++;
+	}
+      else /* skb successfully allocated */
+	{
+	  u_char *ptr;
+      
+	  skb->dev = dev;
+      
+	  /* ptr to start of the sk_buff data area */
+	  ptr = (u_char *)skb_put( skb, pkt_len );
+      
+	  /* Now transfer the data from the card into that area */
+	  if ( lp->mode==2 )
             {
-              if ( lp -> mem_ptr_virt )
-                memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
-               else
-                memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
+              if ( lp->mem_ptr_virt )
+                memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
+              /* Note alignment to 32bit transfers */
+              else
+                memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
             }
-           else
-            insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
-          skb -> protocol = eth_type_trans( skb, dev );
-          netif_rx( skb );
-          lp -> stats.rx_packets++;
+	  else /* io mapped */
+	    insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
+      
+	  skb->protocol = eth_type_trans( skb, dev );
+
+	  netif_rx( skb );
+	  lp->stats.rx_packets++;
+#ifdef LINUX_2_1
+	  lp->stats.rx_bytes += skb->len;
+#endif
+      
 #ifdef HP100_DEBUG_RX
-          printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-		ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
-		ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+	  printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	  	  dev->name,
+		  ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
+		  ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
 #endif
-        }
-      hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+	}
+  
+      /* Indicate the card that we have got the packet */
+      hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+
       switch ( header & 0x00070000 ) {
-        case (HP100_MULTI_ADDR_HASH<<16):
-        case (HP100_MULTI_ADDR_NO_HASH<<16):
-          lp -> stats.multicast++; break;
+      case (HP100_MULTI_ADDR_HASH<<16):
+      case (HP100_MULTI_ADDR_NO_HASH<<16):
+	lp->stats.multicast++; break;
       }
-    }
+    } /* end of while(there are packets) loop */
 #ifdef HP100_DEBUG_RX
-   printk( "hp100_rx: end\n" );
+  printk( "hp100_rx: %s: end\n", dev->name );
+#endif
+}
+
+
+/* 
+ * Receive Function for Busmaster Mode
+ */
+static void hp100_rx_bm( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ptr;
+  u_int header;
+  int pkt_len;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4214, TRACE );
+  printk("hp100: %s: rx_bm\n", dev->name);
+#endif
+
+#ifdef HP100_DEBUG
+  if(0==lp->rxrcommit)
+    {
+      printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name); 
+      return;
+    }
+  else
+
+    /* RX_PKT_CNT states how many PDLs are currently formatted and available to 
+     * the cards BM engine */
+    if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit)
+      {
+	printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit);
+	return;
+      }
 #endif
+
+  while( (lp->rxrcommit > hp100_inb(RX_PDL)) )
+    {
+      /*
+       * The packet was received into the pdl pointed to by lp->rxrhead (
+       * the oldest pdl in the ring 
+       */
+						
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      
+      ptr=lp->rxrhead;
+      
+      header = *(ptr->pdl-1);
+      pkt_len = (header & HP100_PKT_LEN_MASK);
+
+#ifdef HP100_DEBUG_BM
+      printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              (u_int) (ptr->pdl-1),(u_int) header,
+              pkt_len, 
+              (header>>16)&0xfff8,
+              (header>>16)&7);
+      printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
+      	      dev->name,
+	      hp100_inb( RX_PDL ),
+	      hp100_inb( TX_PDL ),
+	      hp100_inb( RX_PKT_CNT ),
+	      (u_int) *(ptr->pdl),
+	      (u_int) *(ptr->pdl+3),
+	      (u_int) *(ptr->pdl+4));
+#endif
+      
+      if( (pkt_len>=MIN_ETHER_SIZE) &&
+          (pkt_len<=MAX_ETHER_SIZE) )  
+        {
+	  if(ptr->skb==NULL)
+	    {
+	      printk("hp100: %s: rx_bm: skb null\n", dev->name);
+	      /* can happen if we only allocated room for the pdh due to memory shortage. */
+	      lp->stats.rx_dropped++;
+	    }
+	  else
+	    {
+	      skb_trim( ptr->skb, pkt_len );     /* Shorten it */
+	      ptr->skb->protocol = eth_type_trans( ptr->skb, dev );
+														
+	      netif_rx( ptr->skb );              /* Up and away... */
+
+	      lp->stats.rx_packets++;
+#ifdef LINUX_2_1
+	      lp->stats.rx_bytes += ptr->skb->len;
+#endif
+	    }
+
+          switch ( header & 0x00070000 ) {
+          case (HP100_MULTI_ADDR_HASH<<16):
+          case (HP100_MULTI_ADDR_NO_HASH<<16):
+            lp->stats.multicast++; break;
+          }
+        }
+      else
+        {
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len);
+#endif
+	  if(ptr->skb!=NULL)
+	    dev_kfree_skb( ptr->skb, FREE_READ );					
+          lp->stats.rx_errors++;
+        }
+						
+      lp->rxrhead=lp->rxrhead->next;
+
+      /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
+      if (0 == hp100_build_rx_pdl( lp->rxrtail, dev ))
+        {
+	  /* No space for skb, header can still be received. */
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name);
+#endif
+	  return;
+        } 
+      else
+        { /* successfully allocated new PDL - put it in ringlist at tail. */
+	  hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA);
+          lp->rxrtail=lp->rxrtail->next;
+	}
+						
+    }
 }
 
+
+
 /*
  *  statistics
  */
- 
-static struct enet_statistics *hp100_get_stats( struct device *dev )
+static hp100_stats_t *hp100_get_stats( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4215, TRACE );
+#endif
 
   hp100_ints_off();
   hp100_update_stats( dev );
   hp100_ints_on();
-  return &((struct hp100_private *)dev -> priv) -> stats;
+  return &((struct hp100_private *)dev->priv)->stats;
 }
 
 static void hp100_update_stats( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
   u_short val;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
-         
-  hp100_page( MAC_CTRL );		/* get all statistics bytes */
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4216, TRACE );
+  printk("hp100: %s: update-stats\n", dev->name);
+#endif
+
+  /* Note: Statistics counters clear when read. */
+  hp100_page( MAC_CTRL ); 
   val = hp100_inw( DROPPED ) & 0x0fff;
-  lp -> stats.rx_errors += val;
-  lp -> stats.rx_over_errors += val;
+  lp->stats.rx_errors += val;
+  lp->stats.rx_over_errors += val;
   val = hp100_inb( CRC );
-  lp -> stats.rx_errors += val;
-  lp -> stats.rx_crc_errors += val;
+  lp->stats.rx_errors += val;
+  lp->stats.rx_crc_errors += val;
   val = hp100_inb( ABORT );
-  lp -> stats.tx_errors += val;
-  lp -> stats.tx_aborted_errors += val;
+  lp->stats.tx_errors += val;
+  lp->stats.tx_aborted_errors += val;
   hp100_page( PERFORMANCE );
 }
 
 static void hp100_clear_stats( int ioaddr )
 {
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4217, TRACE );
+  printk("hp100: %s: clear_stats\n", dev->name);
+#endif
+
   cli();
-  hp100_page( MAC_CTRL );		/* get all statistics bytes */
+  hp100_page( MAC_CTRL );    /* get all statistics bytes */
   hp100_inw( DROPPED );
   hp100_inb( CRC );
   hp100_inb( ABORT );
@@ -838,6 +2071,7 @@
   sti();
 }
 
+
 /*
  *  multicast setup
  */
@@ -848,293 +2082,899 @@
                                                           
 static void hp100_set_multicast_list( struct device *dev)
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
-#ifdef HP100_DEBUG_MULTI
-  printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count);
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4218, TRACE );
+  printk("hp100: %s: set_mc_list\n", dev->name);
 #endif
+
   cli();
   hp100_ints_off();
   hp100_page( MAC_CTRL );
-  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );	/* stop rx/tx */
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
 
-  if ( dev->flags&IFF_PROMISC)
+  if ( dev->flags & IFF_PROMISC )
     {
-      lp -> mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode, all good */
-      lp -> mac1_mode = HP100_MAC1MODE6;  /* packets on the net */
+      lp->mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode = get all good */
+      lp->mac1_mode = HP100_MAC1MODE6;  /* packets on the net */
+      memset( &lp->hash_bytes, 0xff, 8 );
     }
-   else
-  if ( dev->mc_count || dev->flags&IFF_ALLMULTI )
+  else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) )
+    {
+      lp->mac2_mode = HP100_MAC2MODE5;  /* multicast mode = get packets for */
+      lp->mac1_mode = HP100_MAC1MODE5;  /* me, broadcasts and all multicasts */
+#ifdef HP100_MULTICAST_FILTER		/* doesn't work!!! */
+      if ( dev -> flags & IFF_ALLMULTI )
+        {
+          /* set hash filter to receive all multicast packets */
+          memset( &lp->hash_bytes, 0xff, 8 );
+        }
+       else
+        {
+          int i, j, idx;
+          u_char *addrs;
+          struct dev_mc_list *dmi;
+
+          memset( &lp->hash_bytes, 0x00, 8 );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count );
+#endif 
+          for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next )
+            {
+              addrs = dmi -> dmi_addr;
+              if ( ( *addrs & 0x01 ) == 0x01 )	/* multicast address? */
+                {
+#ifdef HP100_DEBUG
+                  printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ",
+                  	dev -> name,
+        		addrs[ 0 ], addrs[ 1 ], addrs[ 2 ],
+        		addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] );
+#endif 
+                  for ( j = idx = 0; j < 6; j++ )
+                    {
+                      idx ^= *addrs++ & 0x3f;
+                      printk( ":%02x:", idx );
+                    }
+#ifdef HP100_DEBUG
+                  printk("idx = %i\n", idx );
+#endif
+		  lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) );
+                }
+            }
+        }
+#else
+      memset( &lp->hash_bytes, 0xff, 8 );
+#endif
+    }
+  else
+    {
+      lp->mac2_mode = HP100_MAC2MODE3;  /* normal mode = get packets for me */
+      lp->mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */
+      memset( &lp->hash_bytes, 0x00, 8 );
+    }
+
+  if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) ||
+        ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) 
     {
-      lp -> mac2_mode = HP100_MAC2MODE5;  /* multicast mode, packets for me */
-      lp -> mac1_mode = HP100_MAC1MODE5;  /* broadcasts and all multicasts */
+      int i;
+    
+      hp100_outb( lp->mac2_mode, MAC_CFG_2 );
+      hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */
+      hp100_orb( lp->mac1_mode, MAC_CFG_1 );       /* and set the new mode */
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+      printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+      		dev->name, lp->mac1_mode, lp->mac2_mode,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+      if(lp->lan_type==HP100_LAN_100)
+        {
+#ifdef HP100_DEBUG
+  	  printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+	  lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+        }
     }
    else
     {
-      lp -> mac2_mode = HP100_MAC2MODE3;  /* normal mode, packets for me */
-      lp -> mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */
+      int i;
+      u_char old_hash_bytes[ 8 ];
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i );
+      if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) )
+        {
+          for ( i = 0; i < 8; i++ )
+            hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+          	dev->name,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+          if(lp->lan_type==HP100_LAN_100)
+            {
+#ifdef HP100_DEBUG
+  	      printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+   	      lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+            }
+        }
     }
 
-  hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
-  hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
-  hp100_orb( lp -> mac1_mode |
-  	     HP100_RX_EN | HP100_RX_IDLE |		/* enable rx */
-  	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );	/* enable tx */
+  hp100_page( MAC_CTRL );
+  hp100_orb( HP100_RX_EN | HP100_RX_IDLE |              /* enable rx */
+	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );  /* enable tx */
+
   hp100_page( PERFORMANCE );
   hp100_ints_on();
   sti();
 }
 
+
 /*
  *  hardware interrupt handling
  */
 
 static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs )
 {
-  struct device *dev = (struct device *)irq2dev_map[ irq ];
-  struct hp100_private *lp;
+  struct device *dev = (struct device *)dev_id;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
   int ioaddr;
-  u_short val;
+  u_int val;
 
   if ( dev == NULL ) return;
-  ioaddr = dev -> base_addr;
-  if ( dev -> interrupt )
-    printk( "%s: re-entering the interrupt handler\n", dev -> name );
+  ioaddr = dev->base_addr;
+
+  if ( dev->interrupt )
+    printk( "hp100: %s: re-entering the interrupt handler\n", dev->name );
   hp100_ints_off();
-  dev -> interrupt = 1;
-  hp100_page( PERFORMANCE );
+  dev->interrupt = 1;          /* mark that we are inside the handler */
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4219, TRACE );
+#endif
+
+  /*  hp100_page( PERFORMANCE ); */
   val = hp100_inw( IRQ_STATUS );
 #ifdef HP100_DEBUG_IRQ
-  printk( "hp100_interrupt: irq_status = 0x%x\n", val );
+  printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
+  	  dev->name,
+          lp->mode,
+	  (u_int)val,
+          hp100_inb( RX_PKT_CNT ),
+          hp100_inb( RX_PDL ),
+	  hp100_inb( TX_PKT_CNT ),
+	  hp100_inb( TX_PDL )
+	  );
 #endif
-  if ( val & HP100_RX_PACKET )
+
+  if(val==0) /* might be a shared interrupt */ 
     {
-      hp100_rx( dev );
-      hp100_outw( HP100_RX_PACKET, IRQ_STATUS );
+      dev->interrupt=0;
+      hp100_ints_on();
+      return;
     }
-  if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) )
-    {
-      hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS );
+  /* We're only interested in those interrupts we really enabled. */
+  /* val &= hp100_inw( IRQ_MASK ); */
+
+  /* 
+   * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL 
+   * is considered executed whenever the RX_PDL data structure is no longer 
+   * needed.
+   */
+  if ( val & HP100_RX_PDL_FILL_COMPL )
+    {
+      if(lp->mode==1)
+	hp100_rx_bm( dev );
+      else
+	{
+	  printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name);
+	}
     }
+		
+  /* 
+   * The RX_PACKET interrupt is set, when the receive packet counter is
+   * non zero. We use this interrupt for receiving in slave mode. In
+   * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
+   * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
+   * we somehow have missed a rx_pdl_fill_compl interrupt.
+   */
+
+  if ( val & HP100_RX_PACKET  ) /* Receive Packet Counter is non zero */
+    {
+      if(lp->mode!=1) /* non busmaster */
+        hp100_rx( dev );
+      else if ( !(val & HP100_RX_PDL_FILL_COMPL ))
+	{
+	  /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt?  */
+	  hp100_rx_bm( dev );
+	}
+    }
+
+  /*
+   * Ack. that we have noticed the interrupt and thereby allow next one.
+   * Note that this is now done after the slave rx function, since first
+   * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
+   * on the J2573.
+   */
+  hp100_outw( val, IRQ_STATUS );
+
+  /*
+   * RX_ERROR is set when a packet is dropped due to no memory resources on 
+   * the card or when a RCV_ERR occurs. 
+   * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists  
+   * only in the 802.3 MAC and happens when 16 collisions occur during a TX 
+   */
   if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
     {
-      lp = (struct hp100_private *)dev -> priv;
-      hp100_update_stats( dev );
-      hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS );
-    }
 #ifdef HP100_DEBUG_IRQ
-  printk( "hp100_interrupt: end\n" );
+      printk("hp100: %s: TX/RX Error IRQ\n", dev->name);
 #endif
-  dev -> interrupt = 0;
+      hp100_update_stats( dev );
+      if(lp->mode==1)
+	{
+	  hp100_rxfill( dev );
+	  hp100_clean_txring( dev );
+	}
+    }
+				
+  /* 
+   * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. 
+   */
+  if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) )
+    hp100_rxfill( dev );
+		
+  /* 
+   * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire 
+   * is completed 
+   */
+  if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) )
+    hp100_clean_txring( dev );
+
+  /* 
+   * MISC_ERROR is set when either the LAN link goes down or a detected
+   * bus error occurs.
+   */
+  if ( val & HP100_MISC_ERROR ) /* New for J2585B */
+    {
+      printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name);
+      if(lp->mode==1)
+	{
+	  hp100_clean_txring( dev );
+	  hp100_rxfill( dev );
+	}
+    }
+	
+  dev->interrupt = 0;
   hp100_ints_on();
 }
 
+
 /*
  *  some misc functions
  */
 
 static void hp100_start_interface( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4220, TRACE );
+  printk("hp100: %s: hp100_start_interface\n",dev->name);
+#endif
 
   cli();
-  hp100_unreset_card();
-  hp100_page( MAC_CTRL );
-  hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
-  hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
-  hp100_orb( lp -> mac1_mode |
-             HP100_RX_EN | HP100_RX_IDLE |
-             HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );
+
+  /* Ensure the adapter does not want to request an interrupt when */
+  /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
   hp100_page( PERFORMANCE );
-  hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW );
-  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );
-  if ( lp -> mem_mapped )
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack all IRQs */
+  hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW);
+  /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
+  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); 
+
+  if(lp->mode==1)
+    {
+      /* Make sure BM bit is set... */
+      hp100_page(HW_MAP);
+      hp100_orb( HP100_BM_MASTER, BM );
+      hp100_rxfill( dev );
+    }
+  else if(lp->mode==2)
     {
-      /* enable memory mapping */
+      /* Enable memory mapping. Note: Don't do this when busmaster. */
       hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
     }
-  sti();
-} 
 
+  hp100_page(PERFORMANCE);
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /* enable a few interrupts: */
+  if(lp->mode==1) /* busmaster mode */
+    {
+      hp100_outw( HP100_RX_PDL_FILL_COMPL |
+		  HP100_RX_PDA_ZERO  |  
+		  HP100_RX_ERROR     |  
+		  /* HP100_RX_PACKET    | */    
+		  /* HP100_RX_EARLY_INT |  */     HP100_SET_HB  |  
+		  /* HP100_TX_PDA_ZERO  |  */
+		  HP100_TX_COMPLETE  |  
+		  /* HP100_MISC_ERROR   |  */
+		  HP100_TX_ERROR     | HP100_SET_LB, IRQ_MASK );
+    } 
+  else
+    {  
+      hp100_outw( HP100_RX_PACKET  |
+		  HP100_RX_ERROR   | HP100_SET_HB |
+                  HP100_TX_ERROR   | HP100_SET_LB , IRQ_MASK );
+    }
+	
+  /* Enable MAC Tx and RX, set MAC modes, ... */
+  /* Note: This function also turns on the interrupts. */
+  hp100_set_multicast_list( dev );
+}
+
+
 static void hp100_stop_interface( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  u_short val;
-
-  hp100_outw( HP100_INT_EN | HP100_RESET_LB | 
-              HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
-  val = hp100_inw( OPTION_LSW );
-  hp100_page( HW_MAP );
-  hp100_andb( HP100_BM_SLAVE, BM );
-  hp100_page( MAC_CTRL );
-  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
-  if ( !(val & HP100_HW_RST) ) return;
-  for ( val = 0; val < 6000; val++ )
-    if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
-                                    (HP100_TX_IDLE | HP100_RX_IDLE) )
-      return;
-  printk( "%s: hp100_stop_interface - timeout\n", dev -> name );
+  struct hp100_private *lp = (struct hp100_private *)dev->priv; 
+  int ioaddr = dev->base_addr;
+  u_int val;
+
+#ifdef HP100_DEBUG_B
+  printk("hp100: %s: hp100_stop_interface\n",dev->name);
+  hp100_outw( 0x4221, TRACE );
+#endif
+
+  if (lp->mode==1) 
+    hp100_BM_shutdown( dev );
+  else
+    {
+      /* Note: MMAP_DIS will be reenabled by start_interface */
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB | 
+                  HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+      val = hp100_inw( OPTION_LSW );
+   
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
+
+      if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */
+      /* ... else: busy wait until idle */
+      for ( val = 0; val < 6000; val++ )
+	if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
+	     (HP100_TX_IDLE | HP100_RX_IDLE) )
+	  {
+	    hp100_page(PERFORMANCE);
+	    return;
+	  }
+      printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name );
+      hp100_page(PERFORMANCE);
+    }
 }
 
+
 static void hp100_load_eeprom( struct device *dev )
 {
   int i;
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4222, TRACE );
+#endif
 
   hp100_page( EEPROM_CTRL );
   hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
   hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
-  for ( i = 0; i < 6000; i++ )
-    if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
-  printk( "%s: hp100_load_eeprom - timeout\n", dev -> name );
+  for ( i = 0; i < 10000; i++ )
+    if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
+  printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name );
 }
 
-/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */
-
+
+/*  Sense connection status.
+ *  return values: LAN_10  - Connected to 10Mbit/s network
+ *                 LAN_100 - Connected to 100Mbit/s network
+ *                 LAN_ERR - not connected or 100Mbit/s Hub down
+ */
 static int hp100_sense_lan( struct device *dev )
 {
-  int i;
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
   u_short val_VG, val_10;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4223, TRACE );
+#endif
 
   hp100_page( MAC_CTRL );
-  hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-  val_10 = hp100_inw( LAN_CFG_10 );
-  val_VG = hp100_inw( LAN_CFG_VG );
-#ifdef HP100_DEBUG_SENSE
-  printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 );
-#endif
-  if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10;
-  if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */
-    return HP100_LAN_ERR;
-  for ( i = 0; i < 2500; i++ )
+  val_10 = hp100_inb( 10_LAN_CFG_1 );
+  val_VG = hp100_inb( VG_LAN_CFG_1 );
+  hp100_page( PERFORMANCE );
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 );
+#endif
+
+  if ( val_10 & HP100_LINK_BEAT_ST )	/* 10Mb connection is active */
+    return HP100_LAN_10;
+
+  if ( val_10 & HP100_AUI_ST )		/* have we BNC or AUI onboard? */
     {
-      val_VG = hp100_inw( LAN_CFG_VG );
-      if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100;
+      val_10 |= HP100_AUI_SEL | HP100_LOW_TH;
+      hp100_page( MAC_CTRL );
+      hp100_outb( val_10, 10_LAN_CFG_1 );
+      hp100_page( PERFORMANCE );
+      return HP100_LAN_10;
     }
+
+  if ( (lp->id->id == 0x02019F022) || 
+       (lp->id->id == 0x01042103c) ||
+       (lp->id->id == 0x01040103c) )
+    return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */
+
+  if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ 
+    return HP100_LAN_100;
   return HP100_LAN_ERR;
 }
 
+
+
 static int hp100_down_vg_link( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int ioaddr = dev->base_addr;
   unsigned long time;
-  int i;
+  long savelan, newlan;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4224, TRACE );
+  printk("hp100: %s: down_vg_link\n", dev->name);
+#endif
 
   hp100_page( MAC_CTRL );
-  for ( i = 2500; i > 0; i-- )
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  if ( i <= 0 )				/* not signal - not logout */
+  time=jiffies+(HZ/4);
+  do{
+    if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+  } while (time>jiffies);
+
+  if ( jiffies >= time )       /* no signal->no logout */
     return 0;
-  hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG );
-  time = jiffies + 10; 
-  while ( time > jiffies )
-    if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST | 
-                                        HP100_LINK_CABLE_ST | 
-                                        HP100_LINK_GOOD_ST ) ) )
-      return 0;
+
+  /* Drop the VG Link by clearing the link up cmd and load addr.*/
+
+  hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); 
+  hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); 
+
+  /* Conditionally stall for >250ms on Link-Up Status (to go down) */
+  time=jiffies+(HZ/2);
+  do{
+    if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break;
+  } while(time>jiffies);
+
 #ifdef HP100_DEBUG
-  printk( "hp100_down_vg_link: timeout\n" );
+  if (jiffies>=time)
+    printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name);
 #endif
-  return -EIO;
-}
 
-static int hp100_login_to_vg_hub( struct device *dev )
-{
-  int i;
-  int ioaddr = dev -> base_addr;
-  u_short val;
-  unsigned long time;  
+  /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
+  /* logout under traffic (even though all the status bits are cleared),  */
+  /* do this workaround to get the Rev 1 MAC in its idle state */
+  if ( lp->chip==HP100_CHIPID_LASSEN )
+    {
+      /* Reset VG MAC to insure it leaves the logoff state even if */
+      /* the Hub is still emitting tones */
+      hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
+      udelay(1500); /* wait for >1ms */
+      hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */
+      udelay(1500);
+    }
+
+  /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
+  /* to get the VG mac to full reset. This is not req.d with later chips */
+  /* Note: It will take the between 1 and 2 seconds for the VG mac to be */
+  /* selected again! This will be left to the connect hub function to */
+  /* perform if desired.  */
+  if (lp->chip==HP100_CHIPID_LASSEN)
+    {
+      /* Have to write to 10 and 100VG control registers simultaneously */
+      savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */
+      newlan &= ~(HP100_VG_SEL<<16);
+      newlan |= (HP100_DOT3_MAC)<<8;
+      hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */
+      hp100_outl(newlan, 10_LAN_CFG_1);
+
+      /* Conditionally stall for 5sec on VG selected. */
+      time=jiffies+(HZ*5);
+      do{
+        if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break;
+      } while(time>jiffies);
 
-  hp100_page( MAC_CTRL );
-  hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-  time = jiffies + ( HZ / 2 );
+      hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
+      hp100_outl(savelan, 10_LAN_CFG_1);
+    }
+
+  time=jiffies+(3*HZ); /* Timeout 3s */
   do {
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  } while ( time > jiffies );
-  if ( time <= jiffies )
+    if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break;
+  } while (time>jiffies);
+  
+  if(time<=jiffies)
     {
 #ifdef HP100_DEBUG
-      printk( "hp100_login_to_vg_hub: timeout for link\n" );
+      printk( "hp100: %s: down_vg_link: timeout\n", dev->name );
 #endif
       return -EIO;
     }
-    
-  if ( hp100_down_vg_link( dev ) < 0 )	/* if fail, try reset VG link */
-    {
-      hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG );
-      hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-    }
-  /* bring up link */
-  hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG );
-  for ( i = 2500; i > 0; i-- )
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  if ( i <= 0 )
+  
+  time=jiffies+(2*HZ); /* This seems to take a while.... */
+  do {} while (time>jiffies);
+  
+  return 0;
+}
+
+
+static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  u_short val=0;
+  unsigned long time;
+  int startst;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4225, TRACE );
+  printk("hp100: %s: login_to_vg_hub\n", dev->name);
+#endif
+
+  /* Initiate a login sequence iff VG MAC is enabled and either Load Address
+   * bit is zero or the force relogin flag is set (e.g. due to MAC address or
+   * promiscuous mode change)
+   */
+  hp100_page( MAC_CTRL );
+  startst=hp100_inb( VG_LAN_CFG_1 );
+  if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST))
     {
-#ifdef HP100_DEBUG
-      printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" );
+#ifdef HP100_DEBUG_TRAINING
+      printk("hp100: %s: Start training\n", dev->name);
 #endif
-      goto down_link;
-    }
 
-  time = jiffies + ( HZ / 2 );
-  do {   
-    val = hp100_inw( LAN_CFG_VG );
-    if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) == 
-                 ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) )
-      return 0;	/* success */
-  } while ( time > jiffies );
-  if ( val & HP100_LINK_GOOD_ST )
-    printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name );
-   else
-    printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name );
+      /* Ensure VG Reset bit is 1 (i.e., do not reset)*/
+      hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 );
+
+      /* If Lassen AND auto-select-mode AND VG tones were sensed on */
+      /* entry then temporarily put them into force 100Mbit mode */
+      if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) )
+        hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 );
+						
+      /* Drop the VG link by zeroing Link Up Command and Load Address  */
+      hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1);
+
+#ifdef HP100_DEBUG_TRAINING
+      printk("hp100: %s: Bring down the link\n", dev->name);
+#endif
+
+      /* Wait for link to drop */
+      time = jiffies + (HZ/10); 
+      do {
+        if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break;
+      } while (time>jiffies);
+
+      /* Start an addressed training and optionally request promiscuous port */
+      if ( (dev->flags) & IFF_PROMISC )
+	{
+	  hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2);
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	}
+      else
+	{
+	  hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2);
+	  /* For ETR parts we need to reset the prom. bit in the training
+	   * register, otherwise promiscious mode won't be disabled.
+	   */
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    {
+	      hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	    }
+	}
+
+      /* With ETR parts, frame format request bits can be set. */
+      if(lp->chip==HP100_CHIPID_LASSEN)
+        hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
+
+      hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1);
+
+      /* Note: Next wait could be omitted for Hood and earlier chips under */
+      /* certain circumstances */
+      /* TODO: check if hood/earlier and skip wait. */
+
+      /* Wait for either short timeout for VG tones or long for login    */
+      /* Wait for the card hardware to signalise link cable status ok... */
+      hp100_page( MAC_CTRL );
+      time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */
+      do {
+        if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+      } while ( jiffies < time );
+      
+      if ( jiffies >= time )
+	{
+#ifdef HP100_DEBUG_TRAINING
+	  printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name );
+#endif  
+	}
+      else
+	{
+#ifdef HP100_DEBUG_TRAINING
+	  printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name);
+#endif
+
+	  time = jiffies + ( 2*HZ ); /* again a timeout */
+	  do {
+	    val = hp100_inb( VG_LAN_CFG_1 );
+	    if ( (val & ( HP100_LINK_UP_ST )) )
+	      {
+#ifdef HP100_DEBUG_TRAINING
+		printk( "hp100: %s: Passed training.\n", dev->name);
+#endif
+		break;
+	      }
+	  } while ( time > jiffies );
+	}
+      
+      /* If LINK_UP_ST is set, then we are logged into the hub. */
+      if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) )
+        {
+#ifdef HP100_DEBUG_TRAINING
+          printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+	      val = hp100_inw(TRAIN_ALLOW);
+              printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ",
+              	      dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre");
+	      printk( "Driver will use MAC Version \"%s\"\n",
+                      ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); 
+              printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3");
+            }
+#endif
+        }
+      else
+        {
+          /* If LINK_UP_ST is not set, login was not successful */
+          printk("hp100: %s: Problem logging into the HUB.\n",dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+              /* Check allowed Register to find out why there is a problem. */
+              val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */
+#ifdef HP100_DEBUG_TRAINING
+              printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val);
+#endif
+              if ( val & HP100_MALLOW_ACCDENIED )
+                printk("hp100: %s: HUB access denied.\n", dev->name);
+              if ( val & HP100_MALLOW_CONFIGURE )
+                printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name);
+              if ( val & HP100_MALLOW_DUPADDR )
+                printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name);
+            }
+        }
+      
+      /* If we have put the chip into forced 100 Mbit mode earlier, go back */
+      /* to auto-select mode */
+      
+      if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) )
+        {
+          hp100_page( MAC_CTRL );
+          hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 );
+        }
+     
+      val=hp100_inb(VG_LAN_CFG_1);
 
-down_link:
-  hp100_down_vg_link( dev );
-  hp100_page( MAC_CTRL );
-  hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG );
-  hp100_orw( HP100_LINK_CMD, LAN_CFG_VG );
+      /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
+      hp100_page(PERFORMANCE);
+      hp100_outw( HP100_MISC_ERROR, IRQ_STATUS);
+					
+      if (val&HP100_LINK_UP_ST)
+        return(0); /* login was ok */
+      else
+        {
+          printk("hp100: %s: Training failed.\n", dev->name);
+          hp100_down_vg_link( dev );
+          return -EIO;
+        }
+    }
+  /* no forced relogin & already link there->no training. */
   return -EIO;
 }
 
+
+static void hp100_cascade_reset( struct device *dev, u_short enable )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int i;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4226, TRACE );
+  printk("hp100: %s: cascade_reset\n", dev->name);
+#endif
+
+  if (enable==TRUE) 
+    {
+      hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW );
+      if(lp->chip==HP100_CHIPID_LASSEN)
+	{
+	  /* Lassen requires a PCI transmit fifo reset */
+	  hp100_page( HW_MAP );
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_orb( HP100_PCI_RESET, PCICTRL2 );
+	  /* Wait for min. 300 ns */
+	  /* we cant use jiffies here, because it may be */
+	  /* that we have disabled the timer... */
+	  for (i=0; i<0xffff; i++);
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_page( PERFORMANCE );
+	}	
+    }
+  else
+    { /* bring out of reset */
+      hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW);
+      for (i=0; i<0xffff; i++ );
+      hp100_page(PERFORMANCE);
+    }
+}
+
+#ifdef HP100_DEBUG		
+void hp100_RegisterDump( struct device *dev )
+{
+  int ioaddr=dev->base_addr;
+  int Page;
+  int Register;
+
+  /* Dump common registers */
+  printk("hp100: %s: Cascade Register Dump\n", dev->name);
+  printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID));
+  printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING));
+  printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW));
+  printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW));
+
+  /* Dump paged registers */
+  for (Page = 0; Page < 8; Page++) 
+    {
+      /* Dump registers */
+      printk("page: 0x%.2x\n",Page);
+      outw( Page, ioaddr+0x02);
+      for (Register = 0x8; Register < 0x22; Register += 2)
+	{
+	  /* Display Register contents except data port */
+	  if (((Register != 0x10) && (Register != 0x12)) || (Page > 0))
+	    {
+	      printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register));
+	    }
+	}
+    }
+  hp100_page(PERFORMANCE);
+}
+#endif
+
+
+
 /*
  *  module section
  */
  
 #ifdef MODULE
 
-static int hp100_port = -1;
+/* Parameters set by insmod */
+int hp100_port[5] = { 0, -1, -1, -1, -1 };
+#ifdef LINUX_2_1
+MODULE_PARM(hp100_port, "1-5i");
+#endif
 
-static char devicename[9] = { 0, };
-static struct device dev_hp100 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, hp100_probe
-};
+#ifdef LINUX_2_1
+char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
+MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
+#else
+static char devname[5][IFNAMSIZ] = { "", "", "", "", "" };
+static char *hp100_name[5] = { devname[0], devname[1],
+                               devname[2], devname[3],
+                               devname[4] };
+#endif
+
+/* List of devices */
+static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
+
+/*
+ * Note: if you have more than five 100vg cards in your pc, feel free to
+ * increase this value 
+ */
+
+/*
+ * Note: to register three eisa or pci devices, use:
+ * option hp100 hp100_port=0,0,0
+ *        to register one card at io 0x280 as eth239, use:
+ * option hp100 hp100_port=0x280 hp100_name=eth239
+ */
 
 int init_module( void )
 {
-  if (hp100_port == 0 && !EISA_bus)
-    printk("HP100: You should not use auto-probing with insmod!\n");
-  if ( hp100_port > 0 )
-    dev_hp100.base_addr = hp100_port;
-  if ( register_netdev( &dev_hp100 ) != 0 )
-    return -EIO;
-  return 0;
-}         
+  int	i;
+  int	ret = 0;
+
+  if (hp100_port == 0 && !EISA_bus && !pcibios_present())
+    printk("hp100: You should not use auto-probing with insmod!\n");
+
+  /* Loop on all possible base addresses */
+  i = -1;
+  while((hp100_port[++i] != -1) && (i < 5))
+    {
+      /* Create device and set basics args */
+      hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
+      memset(hp100_devlist[i], 0x00, sizeof(struct device));
+      hp100_devlist[i]->name = hp100_name[i];
+      hp100_devlist[i]->base_addr = hp100_port[i];
+      hp100_devlist[i]->init = &hp100_probe;
+
+      /* Try to create the device */
+      if(register_netdev(hp100_devlist[i]) != 0)
+	{
+	  /* DeAllocate everything */
+	  /* Note: if dev->priv is mallocated, there is no way to fail */
+	  kfree_s(hp100_devlist[i], sizeof(struct device));
+	  hp100_devlist[i] = (struct device *) NULL;
+	  ret = -EIO;
+	}
+    }			/* Loop over all devices */
+
+  return ret;
+}
 
 void cleanup_module( void )
 {
-  unregister_netdev( &dev_hp100 );
-  release_region( dev_hp100.base_addr, HP100_REGION_SIZE );
-  if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt )
-    vfree( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt );
-  kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) );
-  dev_hp100.priv = NULL;
+  int		i;
+
+  /* TODO: Check if all skb's are released/freed. */
+  for(i = 0; i < 5; i++)
+    if(hp100_devlist[i] != (struct device *) NULL)
+      {
+	unregister_netdev( hp100_devlist[i] );
+	release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE );
+	if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */
+	  kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); 
+	if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt )
+	  iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt );
+	kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) );
+	hp100_devlist[i]->priv = NULL;
+	kfree_s(hp100_devlist[i], sizeof(struct device));
+	hp100_devlist[i] = (struct device *) NULL;
+      }
 }
 
-#endif
+#endif		/* MODULE */
+
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c"
+ *  c-indent-level: 2
+ *  tab-width: 8
+ * End:
+ */
diff -urN linux-2.0.31-pre3/drivers/net/hp100.h linux-2.0.31-pre4/drivers/net/hp100.h
--- linux-2.0.31-pre3/drivers/net/hp100.h	1996-05-06 02:26:08.000000000 -0700
+++ linux-2.0.31-pre4/drivers/net/hp100.h	2003-08-15 15:03:56.000000000 -0700
@@ -1,9 +1,10 @@
 /*
  * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
  *
- * Author:  Jaroslav Kysela, <perex@pf.jcu.cz>
+ * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $
  *
- * Header file...
+ * Authors:  Jaroslav Kysela, <perex@pf.jcu.cz>
+ *           Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de>
  *
  * This driver is based on the 'hpfepkt' crynwr packet driver.
  *
@@ -16,9 +17,10 @@
 /****************************************************************************
  *  Hardware Constants
  ****************************************************************************/
- 
-/*
- *  ATT2MD01 Register Page Constants
+
+/* 
+ * Page Identifiers
+ * (Swap Paging Register, PAGING, bits 3:0, Offset 0x02)
  */
 
 #define HP100_PAGE_PERFORMANCE	0x0	/* Page 0 */
@@ -30,11 +32,8 @@
 #define HP100_PAGE_ID_MAC_ADDR	0x6	/* Page 6 */
 #define HP100_PAGE_MMU_POINTER	0x7	/* Page 7 */
 
-/*
- *  ATT2MD01 Register Addresses
- */
 
-/*  Present on all pages  */
+/* Registers that are present on all pages  */
 
 #define HP100_REG_HW_ID		0x00	/* R:  (16) Unique card ID           */
 #define HP100_REG_TRACE		0x00	/* W:  (16) Used for debug output    */
@@ -47,14 +46,29 @@
 
 #define HP100_REG_IRQ_STATUS	0x08	/* RW: (16) Which ints are pending   */
 #define HP100_REG_IRQ_MASK	0x0a	/* RW: (16) Select ints to allow     */
-#define HP100_REG_FRAGMENT_LEN	0x0c	/* RW: (16)12:0 Current fragment len */
+#define HP100_REG_FRAGMENT_LEN	0x0c	/* W: (16)12:0 Current fragment len */
+/* Note: For 32 bit systems, fragment len and offset registers are available */
+/*       at offset 0x28 and 0x2c, where they can be written as 32bit values. */
 #define HP100_REG_OFFSET	0x0e	/* RW: (16)12:0 Offset to start read */
 #define HP100_REG_DATA32	0x10	/* RW: (32) I/O mode data port       */
 #define HP100_REG_DATA16	0x12	/* RW: WORDs must be read from here  */
 #define HP100_REG_TX_MEM_FREE	0x14	/* RD: (32) Amount of free Tx mem    */
+#define HP100_REG_TX_PDA_L      0x14    /* W: (32) BM: Ptr to PDL, Low Pri  */
+#define HP100_REG_TX_PDA_H      0x1c    /* W: (32) BM: Ptr to PDL, High Pri */
 #define HP100_REG_RX_PKT_CNT	0x18	/* RD: (8) Rx count of pkts on card  */
 #define HP100_REG_TX_PKT_CNT	0x19	/* RD: (8) Tx count of pkts on card  */
-                                        
+#define HP100_REG_RX_PDL        0x1a    /* R: (8) BM: # rx pdl not executed */
+#define HP100_REG_TX_PDL        0x1b    /* R: (8) BM: # tx pdl not executed */
+#define HP100_REG_RX_PDA        0x18    /* W: (32) BM: Up to 31 addresses */
+                                        /*             which point to a PDL */
+#define HP100_REG_SL_EARLY      0x1c    /*    (32) Enhanced Slave Early Rx */
+#define HP100_REG_STAT_DROPPED  0x20    /* R (12) Dropped Packet Counter */
+#define HP100_REG_STAT_ERRORED  0x22    /* R (8) Errored Packet Counter */
+#define HP100_REG_STAT_ABORT    0x23    /* R (8) Abort Counter/OW Coll. Flag */
+#define HP100_REG_RX_RING       0x24    /* W (32) Slave: RX Ring Pointers */
+#define HP100_REG_32_FRAGMENT_LEN 0x28  /* W (13) Slave: Fragment Length Reg */
+#define HP100_REG_32_OFFSET     0x2c    /* W (16) Slave: Offset Register */
+
 /*  Page 1 - MAC Address/Hash Table  */
 
 #define HP100_REG_MAC_ADDR	0x08	/* RW: (8) Cards MAC address	     */
@@ -68,27 +82,46 @@
 #define HP100_REG_IRQ_CHANNEL	0x0d	/* RW: (8) IRQ and edge/level int    */
 #define HP100_REG_SRAM		0x0e	/* RW: (8) How much RAM on card      */
 #define HP100_REG_BM		0x0f	/* RW: (8) Controls BM functions     */
+
+/* New on Page 2 for ETR chips: */
+#define HP100_REG_MODECTRL1     0x10    /* RW: (8) Mode Control 1 */
+#define HP100_REG_MODECTRL2     0x11    /* RW: (8) Mode Control 2 */
+#define HP100_REG_PCICTRL1      0x12    /* RW: (8) PCI Cfg 1 */
+#define HP100_REG_PCICTRL2      0x13    /* RW: (8) PCI Cfg 2 */
+#define HP100_REG_PCIBUSMLAT    0x15    /* RW: (8) PCI Bus Master Latency */
+#define HP100_REG_EARLYTXCFG    0x16    /* RW: (16) Early TX Cfg/Cntrl Reg */
+#define HP100_REG_EARLYRXCFG    0x18    /* RW: (8) Early RX Cfg/Cntrl Reg */
+#define HP100_REG_ISAPNPCFG1    0x1a    /* RW: (8) ISA PnP Cfg/Cntrl Reg 1 */
+#define HP100_REG_ISAPNPCFG2    0x1b    /* RW: (8) ISA PnP Cfg/Cntrl Reg 2 */
                                         
 /*  Page 3 - EEPROM/Boot ROM  */
 
 #define HP100_REG_EEPROM_CTRL	0x08	/* RW: (16) Used to load EEPROM      */
+#define HP100_REG_BOOTROM_CTRL  0x0a
                                         
-/*  Page 4 - LAN Configuration  */
+/*  Page 4 - LAN Configuration  (MAC_CTRL) */
 
-#define HP100_REG_LAN_CFG_10	0x08	/* RW: (16) Set 10M XCVR functions   */
-#define HP100_REG_LAN_CFG_VG	0x0a	/* RW: (16) Set 100M XCVR functions  */
+#define HP100_REG_10_LAN_CFG_1	0x08	/* RW: (8) Set 10M XCVR functions   */
+#define HP100_REG_10_LAN_CFG_2  0x09    /* RW: (8)     10M XCVR functions   */
+#define HP100_REG_VG_LAN_CFG_1	0x0a	/* RW: (8) Set 100M XCVR functions  */
+#define HP100_REG_VG_LAN_CFG_2  0x0b    /* RW: (8) 100M LAN Training cfgregs */
 #define HP100_REG_MAC_CFG_1	0x0c	/* RW: (8) Types of pkts to accept   */
 #define HP100_REG_MAC_CFG_2	0x0d	/* RW: (8) Misc MAC functions        */
-/* The follow clear when read: */
+#define HP100_REG_MAC_CFG_3     0x0e    /* RW: (8) Misc MAC functions */
+#define HP100_REG_MAC_CFG_4     0x0f    /* R:  (8) Misc MAC states */
 #define HP100_REG_DROPPED	0x10	/* R:  (16),11:0 Pkts cant fit in mem*/
 #define HP100_REG_CRC		0x12	/* R:  (8) Pkts with CRC             */
 #define HP100_REG_ABORT		0x13	/* R:  (8) Aborted Tx pkts           */
-                                        
+#define HP100_REG_TRAIN_REQUEST 0x14    /* RW: (16) Endnode MAC register.*/
+#define HP100_REG_TRAIN_ALLOW   0x16    /* R:  (16) Hub allowed register */
+                    
 /*  Page 5 - MMU  */
 
 #define HP100_REG_RX_MEM_STOP	0x0c	/* RW: (16) End of Rx ring addr      */
 #define HP100_REG_TX_MEM_STOP	0x0e	/* RW: (16) End of Tx ring addr      */
-                                        
+#define HP100_REG_PDL_MEM_STOP  0x10    /* Not used by 802.12 devices */
+#define HP100_REG_ECB_MEM_STOP  0x14    /* I've no idea what this is */
+          
 /*  Page 6 - Card ID/Physical LAN Address  */
 
 #define HP100_REG_BOARD_ID	0x08	/* R:  (8) EISA/ISA card ID          */
@@ -99,32 +132,46 @@
                                         
 /*  Page 7 - MMU Current Pointers  */
 
-#define HP100_REG_RX_MEM_BR	0x08	/* R:  (16) Current begin of Rx ring */
-#define HP100_REG_RX_MEM_ER	0x0a	/* R:  (16) Current end of Rx ring   */
-#define HP100_REG_TX_MEM_BR	0x0c	/* R:  (16) Current begin of Tx ring */
-#define HP100_REG_TX_MEM_ER	0x0e	/* R:  (16) Current end of Rx ring   */
-#define HP100_REG_MEM_DEBUG	0x1a	/* RW: (16) Used for memory tests    */
-                                                
-/*
- *  HardwareIDReg bits/masks
- */
-
-#define HP100_HW_ID_0		0x50	/* Hardware ID bytes.                */
-#define HP100_HW_ID_1		0x48
-#define HP100_HW_ID_2_REVA	0x50	/* Rev. A ID. NOTE: lower nibble not used */
-#define HP100_HW_ID_3		0x53
-
-/*
- *  OptionLSWReg bits/masks
- */
-
-#define HP100_DEBUG_EN		0x8000	/* 0:Disable, 1:Enable Debug Dump Pointer */
-#define HP100_RX_HDR		0x4000	/* 0:Disable, 1:Enable putting pkt into */
-                                        /*   system memory before Rx interrupt */
-#define HP100_MMAP_DIS		0x2000	/* 0:Enable, 1:Disable memory mapping. */
-                                        /*   MMAP_DIS must be 0 and MEM_EN must */
-                                        /*   be 1 for memory-mapped mode to be */
-                                        /*   enabled */
+#define HP100_REG_PTR_RXSTART	0x08	/* R:  (16) Current begin of Rx ring */
+#define HP100_REG_PTR_RXEND	0x0a	/* R:  (16) Current end of Rx ring   */
+#define HP100_REG_PTR_TXSTART	0x0c	/* R:  (16) Current begin of Tx ring */
+#define HP100_REG_PTR_TXEND	0x0e	/* R:  (16) Current end of Rx ring   */
+#define HP100_REG_PTR_RPDLSTART 0x10
+#define HP100_REG_PTR_RPDLEND   0x12
+#define HP100_REG_PTR_RINGPTRS  0x14
+#define HP100_REG_PTR_MEMDEBUG  0x1a
+/* ------------------------------------------------------------------------ */
+
+
+/* 
+ * Hardware ID Register I (Always available, HW_ID, Offset 0x00)
+ */
+#define HP100_HW_ID_CASCADE     0x4850  /* Identifies Cascade Chip */
+
+/* 
+ * Hardware ID Register 2 & Paging Register
+ * (Always available, PAGING, Offset 0x02)
+ * Bits 15:4 are for the Chip ID 
+ */
+#define HP100_CHIPID_MASK        0xFFF0
+#define HP100_CHIPID_SHASTA      0x5350  /* Not 802.12 compliant */
+                                         /* EISA BM/SL, MCA16/32 SL, ISA SL */
+#define HP100_CHIPID_RAINIER     0x5360  /* Not 802.12 compliant EISA BM,*/
+                                         /* PCI SL, MCA16/32 SL, ISA SL */
+#define HP100_CHIPID_LASSEN      0x5370  /* 802.12 compliant PCI BM, PCI SL */
+                                         /* LRF supported */
+
+/*
+ *  Option Registers I and II 
+ * (Always available, OPTION_LSW, Offset 0x04-0x05)
+ */
+#define HP100_DEBUG_EN		0x8000	/* 0:Dis., 1:Enable Debug Dump Ptr. */
+#define HP100_RX_HDR		0x4000	/* 0:Dis., 1:Enable putting pkt into */
+                                        /*   system mem. before Rx interrupt */
+#define HP100_MMAP_DIS		0x2000	/* 0:Enable, 1:Disable mem.mapping. */
+                                        /*   MMAP_DIS must be 0 and MEM_EN */
+                                        /*   must be 1 for memory-mapped */
+                                        /*   mode to be enabled */
 #define HP100_EE_EN		0x1000	/* 0:Disable,1:Enable EEPROM writing */
 #define HP100_BM_WRITE		0x0800	/* 0:Slave, 1:Bus Master for Tx data */
 #define HP100_BM_READ		0x0400	/* 0:Slave, 1:Bus Master for Rx data */
@@ -132,121 +179,236 @@
 #define HP100_MEM_EN		0x0040	/* Config program set this to */
                                         /*   0:Disable, 1:Enable mem map. */
                                         /*   See MMAP_DIS. */
-#define HP100_IO_EN		0x0020	/* 0:Disable, 1:Enable I/O transfers */
-#define HP100_BOOT_EN		0x0010	/* 0:Disable, 1:Enable boot ROM access */
-#define HP100_FAKE_INT		0x0008	/* 0:No int, 1:int */
-#define HP100_INT_EN		0x0004	/* 0:Disable, 1:Enable ints from card */
+#define HP100_IO_EN		0x0020	/* 1:Enable I/O transfers */
+#define HP100_BOOT_EN		0x0010	/* 1:Enable boot ROM access */
+#define HP100_FAKE_INT		0x0008	/* 1:int */
+#define HP100_INT_EN		0x0004	/* 1:Enable ints from card */
 #define HP100_HW_RST		0x0002	/* 0:Reset, 1:Out of reset */
+                                        /* NIC reset on 0 to 1 transition */
 
 /*
- *  OptionMSWReg bits/masks
+ *  Option Register III 
+ * (Always available, OPTION_MSW, Offset 0x06)
  */
-#define HP100_PRIORITY_TX	0x0080	/* 0:Don't, 1:Do all Tx pkts as priority */
+#define HP100_PRIORITY_TX	0x0080	/* 1:Do all Tx pkts as priority */
 #define HP100_EE_LOAD		0x0040	/* 1:EEPROM loading, 0 when done */
-#define HP100_ADV_NXT_PKT	0x0004	/* 1:Advance to next pkt in Rx queue, */
+#define HP100_ADV_NXT_PKT	0x0004	/* 1:Advance to next pkt in Rx queue */
                                         /*   h/w will set to 0 when done */
-#define HP100_TX_CMD		0x0002	/* 1:Tell h/w download done, h/w will set */
-                                        /*   to 0 when done */
+#define HP100_TX_CMD		0x0002	/* 1:Tell h/w download done, h/w */
+                                        /*   will set to 0 when done */
 
 /*
- *  InterruptStatusReg/InterruptMaskReg bits/masks.  These bits will 0 when a 1 
- *  is written to them.
- */
+ * Interrupt Status Registers I and II
+ * (Page PERFORMANCE, IRQ_STATUS, Offset 0x08-0x09)
+ * Note: With old chips, these Registers will clear when 1 is written to them
+ *       with new chips this depends on setting of CLR_ISMODE
+ */
+#define HP100_RX_EARLY_INT      0x2000
+#define HP100_RX_PDA_ZERO       0x1000
+#define HP100_RX_PDL_FILL_COMPL 0x0800
 #define HP100_RX_PACKET		0x0400	/* 0:No, 1:Yes pkt has been Rx */
 #define HP100_RX_ERROR		0x0200	/* 0:No, 1:Yes Rx pkt had error */
+#define HP100_TX_PDA_ZERO       0x0020  /* 1 when PDA count goes to zero */
 #define HP100_TX_SPACE_AVAIL	0x0010	/* 0:<8192, 1:>=8192 Tx free bytes */
 #define HP100_TX_COMPLETE	0x0008	/* 0:No, 1:Yes a Tx has completed */
+#define HP100_MISC_ERROR        0x0004  /* 0:No, 1:Lan Link down or bus error*/
 #define HP100_TX_ERROR		0x0002	/* 0:No, 1:Yes Tx pkt had error */
-                                        
+
 /*
- *  TxMemoryFreeCountReg bits/masks.
+ * Xmit Memory Free Count
+ * (Page PERFORMANCE, TX_MEM_FREE, Offset 0x14) (Read only, 32bit)
  */
-#define HP100_AUTO_COMPARE	0x8000	/* Says at least 8k is available for Tx. */
-                                        /*   NOTE: This mask is for the upper */
-                                        /*   word of the register. */
+#define HP100_AUTO_COMPARE	0x80000000	/* Tx Space avail & pkts<255 */
+#define HP100_FREE_SPACE	0x7fffffe0	/* Tx free memory */
 
 /*
- *  IRQChannelReg bits/masks.
+ *  IRQ Channel
+ * (Page HW_MAP, IRQ_CHANNEL, Offset 0x0d)
  */
 #define HP100_ZERO_WAIT_EN	0x80	/* 0:No, 1:Yes asserts NOWS signal */
+#define HP100_IRQ_SCRAMBLE      0x40
+#define HP100_BOND_HP           0x20
 #define HP100_LEVEL_IRQ		0x10	/* 0:Edge, 1:Level type interrupts. */
-                                        /*   Only valid on EISA cards. */
-#define HP100_IRQ_MASK		0x0F	/* Isolate the IRQ bits */
+                                        /* (Only valid on EISA cards) */
+#define HP100_IRQMASK		0x0F	/* Isolate the IRQ bits */
 
 /*
- *  SRAMReg bits/masks.
+ * SRAM Parameters
+ * (Page HW_MAP, SRAM, Offset 0x0e)
  */
 #define HP100_RAM_SIZE_MASK	0xe0	/* AND to get SRAM size index */
-#define HP100_RAM_SIZE_SHIFT	0x05	/* Shift count to put index in lower bits */
+#define HP100_RAM_SIZE_SHIFT	0x05	/* Shift count(put index in lwr bits)*/
+
+/*
+ * Bus Master Register
+ * (Page HW_MAP, BM, Offset 0x0f)
+ */
+#define HP100_BM_BURST_RD       0x01    /* EISA only: 1=Use burst trans. fm system */
+                                        /* memory to chip (tx) */
+#define HP100_BM_BURST_WR       0x02    /* EISA only: 1=Use burst trans. fm system */
+                                        /* memory to chip (rx) */
+#define HP100_BM_MASTER		0x04	/* 0:Slave, 1:BM mode */
+#define HP100_BM_PAGE_CK        0x08    /* This bit should be set whenever in*/
+                                        /* an EISA system */
+#define HP100_BM_PCI_8CLK       0x40    /* ... cycles 8 clocks apart */
+
+
+/* 
+ * Mode Control Register I
+ * (Page HW_MAP, MODECTRL1, Offset0x10)
+ */
+#define HP100_TX_DUALQ          0x10
+   /* If set and BM -> dual tx pda queues*/
+#define HP100_ISR_CLRMODE       0x02   /* If set ISR will clear all pending */
+                                       /* interrupts on read (etr only?) */
+#define HP100_EE_NOLOAD         0x04   /* Status whether res will be loaded */
+                                       /* from the eeprom */
+#define HP100_TX_CNT_FLG        0x08   /* Controls Early TX Reg Cnt Field */
+#define HP100_PDL_USE3          0x10   /* If set BM engine will read only */
+                                       /* first three data elements of a PDL */
+                                       /* on the first access. */
+#define HP100_BUSTYPE_MASK      0xe0   /* Three bit bus type info */
+
+/*
+ * Mode Control Register II
+ * (Page HW_MAP, MODECTRL2, Offset0x11)
+ */
+#define HP100_EE_MASK           0x0f   /* Tell EEPROM circuit not to load */ 
+                                       /* certain resources */
+#define HP100_DIS_CANCEL        0x20   /* For tx dualq mode operation */
+#define HP100_EN_PDL_WB         0x40   /* 1: Status of PDL completion may be */
+                                       /* written back to system mem */
+#define HP100_EN_BUS_FAIL       0x80   /* Enables bus-fail portion of misc */
+                                       /* interrupt */
+
+/* 
+ * PCI Configuration and Control Register I
+ * (Page HW_MAP, PCICTRL1, Offset 0x12)
+ */
+#define HP100_LO_MEM            0x01   /* 1: Mapped Mem requested below 1MB */
+#define HP100_NO_MEM            0x02   /* 1: Disables Req for sysmem to PCI */ 
+                                       /* bios */
+#define HP100_USE_ISA           0x04   /* 1: isa type decodes will occur */
+                                       /* simultaneously with PCI decodes */
+#define HP100_IRQ_HI_MASK       0xf0   /* pgmed by pci bios */
+#define HP100_PCI_IRQ_HI_MASK   0x78    /* Isolate 4 bits for PCI IRQ  */
+ 
+/*
+ * PCI Configuration and Control Register II
+ * (Page HW_MAP, PCICTRL2, Offset 0x13)
+ */
+#define HP100_RD_LINE_PDL       0x01   /* 1: PCI command Memory Read Line en */
+#define HP100_RD_TX_DATA_MASK   0x06   /* choose PCI memread cmds for TX */ 
+#define HP100_MWI               0x08   /* 1: en. PCI memory write invalidate */
+#define HP100_ARB_MODE          0x10   /* Select PCI arbitor type */
+#define HP100_STOP_EN           0x20   /* Enables PCI state machine to issue */
+                                       /* pci stop if cascade not ready */
+#define HP100_IGNORE_PAR        0x40   /* 1: PCI state machine ignores parity*/
+#define HP100_PCI_RESET         0x80   /* 0->1: Reset PCI block */
+
+/*
+ * Early TX Configuration and Control Register
+ * (Page HW_MAP, EARLYTXCFG, Offset 0x16)
+ */
+#define HP100_EN_EARLY_TX       0x8000  /* 1=Enable Early TX */
+#define HP100_EN_ADAPTIVE       0x4000  /* 1=Enable adaptive mode */
+#define HP100_EN_TX_UR_IRQ      0x2000  /* reserved, must be 0 */
+#define HP100_EN_LOW_TX         0x1000  /* reserved, must be 0 */
+#define HP100_ET_CNT_MASK       0x0fff  /* bits 11..0: ET counters */
 
 /*
- *  BMReg bits/masks.
+ * Early RX Configuration and Control Register
+ * (Page HW_MAP, EARLYRXCFG, Offset 0x18)
  */
-#define HP100_BM_SLAVE		0x04	/* 0:Slave, 1:BM mode */
+#define HP100_EN_EARLY_RX       0x80     /* 1=Enable Early RX */
+#define HP100_EN_LOW_RX         0x40     /* reserved, must be 0 */
+#define HP100_RX_TRIP_MASK      0x1f     /* bits 4..0: threshold at which the
+					  * early rx circuit will start the
+					  * dma of received packet into system
+					  * memory for BM */
 
 /*
- *  EEPROMControlReg bits/masks.
+ *  Serial Devices Control Register
+ * (Page EEPROM_CTRL, EEPROM_CTRL, Offset 0x08)
  */
-#define HP100_EEPROM_LOAD	0x0001	/* 0->1 loads the EEPROM into registers. */
-                                        /*   When it goes back to 0, load is  */
-                                        /*   complete.  This should take ~600us. */
+#define HP100_EEPROM_LOAD	0x0001	/* 0->1 loads EEPROM into registers. */
+                                        /* When it goes back to 0, load is   */
+                                        /* complete. This should take ~600us.*/
 
 /*
- *  LANCntrCfg10Reg bits/masks.
+ * 10MB LAN Control and Configuration Register I
+ * (Page MAC_CTRL, 10_LAN_CFG_1, Offset 0x08)
  */
-#define HP100_SQU_ST		0x0100	/* 0:No, 1:Yes collision signal sent */
-                                        /*   after Tx.  Only used for AUI. */
-#define HP100_MAC10_SEL		0x00c0	/* Get bits to indicate MAC */
-#define HP100_AUI_SEL		0x0020	/* Status of AUI selection */
-#define HP100_LOW_TH		0x0010	/* 0:No, 1:Yes allow better cabling */
-#define HP100_LINK_BEAT_DIS	0x0008	/* 0:Enable, 1:Disable link beat */
-#define HP100_LINK_BEAT_ST	0x0004	/* 0:No, 1:Yes link beat being Rx */
-#define HP100_R_ROL_ST		0x0002	/* 0:No, 1:Yes Rx twisted pair has been */
-                                        /*   reversed */
-#define HP100_AUI_ST		0x0001	/* 0:No, 1:Yes use AUI on TP card */
+#define HP100_MAC10_SEL		0xc0	/* Get bits to indicate MAC */
+#define HP100_AUI_SEL		0x20	/* Status of AUI selection */
+#define HP100_LOW_TH		0x10	/* 0:No, 1:Yes allow better cabling */
+#define HP100_LINK_BEAT_DIS	0x08	/* 0:Enable, 1:Disable link beat */
+#define HP100_LINK_BEAT_ST	0x04	/* 0:No, 1:Yes link beat being Rx */
+#define HP100_R_ROL_ST		0x02	/* 0:No, 1:Yes Rx twisted pair has */
+                                        /*             been reversed */
+#define HP100_AUI_ST		0x01	/* 0:No, 1:Yes use AUI on TP card */
 
-/* MAC Selection, use with MAC10_SEL bits */
+/*
+ * 10 MB LAN Control and Configuration Register II
+ * (Page MAC_CTRL, 10_LAN_CFG_2, Offset 0x09)
+ */
+#define HP100_SQU_ST		0x01	/* 0:No, 1:Yes collision signal sent */
+                                        /*       after Tx.Only used for AUI. */
+#define HP100_FULLDUP           0x02    /* 1: LXT901 XCVR fullduplx enabled */
+#define HP100_DOT3_MAC          0x04    /* 1: DOT 3 Mac sel. unless Autosel */
+
+/*
+ * MAC Selection, use with MAC10_SEL bits
+ */
 #define HP100_AUTO_SEL_10	0x0	/* Auto select */
 #define HP100_XCVR_LXT901_10	0x1	/* LXT901 10BaseT transceiver */
 #define HP100_XCVR_7213		0x2	/* 7213 transceiver */
 #define HP100_XCVR_82503	0x3	/* 82503 transceiver */
 
-
 /*
- *  LANCntrCfgVGReg bits/masks.
+ *  100MB LAN Training Register
+ * (Page MAC_CTRL, VG_LAN_CFG_2, Offset 0x0b) (old, pre 802.12)
  */
-#define HP100_FRAME_FORMAT	0x0800	/* 0:802.3, 1:802.5 frames */
-#define HP100_BRIDGE		0x0400	/* 0:No, 1:Yes tell hub it's a bridge */
-#define HP100_PROM_MODE		0x0200	/* 0:No, 1:Yes tell hub card is */
-                                        /*   promiscuous */
-#define HP100_REPEATER		0x0100	/* 0:No, 1:Yes tell hub MAC wants to be */
-                                        /*   a cascaded repeater */
-#define HP100_MAC100_SEL	0x0080	/* 0:No, 1:Yes use 100 Mbit MAC */
-#define HP100_LINK_UP_ST	0x0040	/* 0:No, 1:Yes endnode logged in */
-#define HP100_LINK_CABLE_ST	0x0020	/* 0:No, 1:Yes cable can hear tones from */
-                                        /*   hub */
-#define HP100_LOAD_ADDR		0x0010	/* 0->1 card addr will be sent to hub. */
-                                        /*   100ms later the link status bits are */
-                                        /*   valid */
-#define HP100_LINK_CMD		0x0008	/* 0->1 link will attempt to log in. */
-                                        /*   100ms later the link status bits are */
-                                        /*   valid */
-#define HP100_LINK_GOOD_ST	0x0002	/* 0:No, 1:Yes cable passed training */
-#define HP100_VG_RESET		0x0001	/* 0:Yes, 1:No reset the 100VG MAC */
+#define HP100_FRAME_FORMAT	0x08	/* 0:802.3, 1:802.5 frames */
+#define HP100_BRIDGE		0x04	/* 0:No, 1:Yes tell hub i am a bridge */
+#define HP100_PROM_MODE		0x02	/* 0:No, 1:Yes tell hub card is */
+                                        /*         promiscuous */
+#define HP100_REPEATER		0x01	/* 0:No, 1:Yes tell hub MAC wants to */
+                                        /*         be a cascaded repeater */
+
+/*
+ * 100MB LAN Control and Configuration Register
+ * (Page MAC_CTRL, VG_LAN_CFG_1, Offset 0x0a) 
+ */
+#define HP100_VG_SEL	        0x80	/* 0:No, 1:Yes use 100 Mbit MAC */
+#define HP100_LINK_UP_ST	0x40	/* 0:No, 1:Yes endnode logged in */
+#define HP100_LINK_CABLE_ST	0x20	/* 0:No, 1:Yes cable can hear tones */
+                                        /*         from  hub */
+#define HP100_LOAD_ADDR		0x10	/* 0->1 card addr will be sent  */
+                                        /* 100ms later the link status  */
+                                        /* bits are valid */
+#define HP100_LINK_CMD		0x08	/* 0->1 link will attempt to log in. */
+                                        /* 100ms later the link status */
+                                        /* bits are valid */
+#define HP100_TRN_DONE          0x04    /* NEW ETR-Chips only: Will be reset */
+                                        /* after LinkUp Cmd is given and set */
+                                        /* when training has completed. */
+#define HP100_LINK_GOOD_ST	0x02	/* 0:No, 1:Yes cable passed training */
+#define HP100_VG_RESET		0x01	/* 0:Yes, 1:No reset the 100VG MAC */
 
 
 /*
- *  MACConfiguration1Reg bits/masks.
+ *  MAC Configuration Register I
+ * (Page MAC_CTRL, MAC_CFG_1, Offset 0x0c)
  */
 #define HP100_RX_IDLE		0x80	/* 0:Yes, 1:No currently receiving pkts */
 #define HP100_TX_IDLE		0x40	/* 0:Yes, 1:No currently Txing pkts */
-#define HP100_RX_EN		0x20	/* 0:No, 1:Yes allow receiving of pkts */
-#define HP100_TX_EN		0x10	/* 0:No, 1:Yes allow transmitting of pkts */
+#define HP100_RX_EN		0x20	/* 1: allow receiving of pkts */
+#define HP100_TX_EN		0x10	/* 1: allow transmitting of pkts */
 #define HP100_ACC_ERRORED	0x08	/* 0:No, 1:Yes allow Rx of errored pkts */
 #define HP100_ACC_MC		0x04	/* 0:No, 1:Yes allow Rx of multicast pkts */
 #define HP100_ACC_BC		0x02	/* 0:No, 1:Yes allow Rx of broadcast pkts */
-#define HP100_ACC_PHY		0x01	/* 0:No, 1:Yes allow Rx of ALL physical pkts */
-
+#define HP100_ACC_PHY		0x01	/* 0:No, 1:Yes allow Rx of ALL phys. pkts */
 #define HP100_MAC1MODEMASK	0xf0	/* Hide ACC bits */
 #define HP100_MAC1MODE1		0x00	/* Receive nothing, must also disable RX */
 #define HP100_MAC1MODE2		0x00
@@ -254,15 +416,14 @@
 #define HP100_MAC1MODE4		HP100_MAC1MODE3 | HP100_ACC_MC
 #define HP100_MAC1MODE5		HP100_MAC1MODE4 /* set mc hash to all ones also */
 #define HP100_MAC1MODE6		HP100_MAC1MODE5 | HP100_ACC_PHY	/* Promiscuous */
-
 /* Note MODE6 will receive all GOOD packets on the LAN. This really needs
    a mode 7 defined to be LAN Analyzer mode, which will receive errored and
    runt packets, and keep the CRC bytes. */
-
-#define HP100_MAC1MODE7		MAC1MODE6 OR ACC_ERRORED
+#define HP100_MAC1MODE7		HP100_MAC1MODE6 | HP100_ACC_ERRORED
 
 /*
- *  MACConfiguration2Reg bits/masks.
+ *  MAC Configuration Register II 
+ * (Page MAC_CTRL, MAC_CFG_2, Offset 0x0d)
  */
 #define HP100_TR_MODE		0x80	/* 0:No, 1:Yes support Token Ring formats */
 #define HP100_TX_SAME		0x40	/* 0:No, 1:Yes Tx same packet continuous */
@@ -270,9 +431,12 @@
                                         /*   transceiver */
 #define HP100_LBK_MAC		0x10	/* 0:No, 1:Yes loopback through MAC */
 #define HP100_CRC_I		0x08	/* 0:No, 1:Yes inhibit CRC on Tx packets */
+#define HP100_ACCNA             0x04    /* 1: For 802.5: Accept only token ring
+					 * group addr that maches NA mask */
 #define HP100_KEEP_CRC		0x02	/* 0:No, 1:Yes keep CRC on Rx packets. */
                                         /*   The length will reflect this. */
-
+#define HP100_ACCFA             0x01    /* 1: For 802.5: Accept only functional
+					 * addrs that match FA mask (page1) */
 #define HP100_MAC2MODEMASK	0x02
 #define HP100_MAC2MODE1		0x00
 #define HP100_MAC2MODE2		0x00
@@ -283,6 +447,65 @@
 #define HP100_MAC2MODE7		KEEP_CRC
 
 /*
+ * MAC Configuration Register III 
+ * (Page MAC_CTRL, MAC_CFG_3, Offset 0x0e) 
+ */
+#define HP100_PACKET_PACE       0x03     /* Packet Pacing:
+					  * 00: No packet pacing
+                                          * 01: 8 to 16 uS delay
+                                          * 10: 16 to 32 uS delay
+                                          * 11: 32 to 64 uS delay
+					  */
+#define HP100_LRF_EN            0x04     /* 1: External LAN Rcv Filter and
+					  * TCP/IP Checksumming enabled. */
+#define HP100_AUTO_MODE         0x10     /* 1: AutoSelect between 10/100 */
+
+/*
+ * MAC Configuration Register IV 
+ * (Page MAC_CTRL, MAC_CFG_4, Offset 0x0f)
+ */
+#define HP100_MAC_SEL_ST        0x01      /* (R): Status of external VGSEL
+					   * Signal, 1=100VG, 0=10Mbit sel. */
+#define HP100_LINK_FAIL_ST      0x02      /* (R): Status of Link Fail portion
+                                           * of the Misc. Interrupt */
+
+/* 
+ *  100 MB LAN Training Request/Allowed Registers 
+ * (Page MAC_CTRL, TRAIN_REQUEST and TRAIN_ALLOW, Offset 0x14-0x16)(ETR parts only)
+ */
+#define HP100_MACRQ_REPEATER         0x0001  /* 1: MAC tells HUB it wants to be 
+				              *    a cascaded repeater
+					      * 0: ... wants to be a DTE */
+#define HP100_MACRQ_PROMSC           0x0006  /* 2 bits: Promiscious mode
+					      * 00: Rcv only unicast packets
+					      *     specifically addr to this
+					      *     endnode
+					      * 10: Rcv all pckts fwded by 
+					      *     the local repeater */
+#define HP100_MACRQ_FRAMEFMT_EITHER  0x0018  /* 11: either format allowed */
+#define HP100_MACRQ_FRAMEFMT_802_3   0x0000  /* 00: 802.3 is requested */
+#define HP100_MACRQ_FRAMEFMT_802_5   0x0010  /* 10: 802.5 format is requested */
+#define HP100_CARD_MACVER            0xe000  /* R: 3 bit Cards 100VG MAC version */
+#define HP100_MALLOW_REPEATER        0x0001  /* If reset, requested access as an
+					      * end node is allowed */
+#define HP100_MALLOW_PROMSC          0x0004  /* 2 bits: Promiscious mode
+					      * 00: Rcv only unicast packets
+					      *     specifically addr to this
+					      *     endnode
+					      * 10: Rcv all pckts fwded by 
+					      *     the local repeater */
+#define HP100_MALLOW_FRAMEFMT        0x00e0  /* 2 bits: Frame Format
+					      * 00: 802.3 format will be used
+					      * 10: 802.5 format will be used */
+#define HP100_MALLOW_ACCDENIED       0x0400  /* N bit */
+#define HP100_MALLOW_CONFIGURE       0x0f00  /* C bit */
+#define HP100_MALLOW_DUPADDR         0x1000  /* D bit */
+#define HP100_HUB_MACVER             0xe000  /* R: 3 bit 802.12 MAC/RMAC training */
+                                             /*    protocol of repeater */
+
+/* ****************************************************************************** */
+
+/*
  *  Set/Reset bits
  */
 #define HP100_SET_HB		0x0100	/* 0:Set fields to 0 whose mask is 1 */
@@ -297,20 +520,45 @@
 #define HP100_LAN_10		10	/* lan_type value for 10BaseT */
 #define HP100_LAN_ERR		(-1)	/* lan_type value for link down */
 
-/*
- *  Receive Header Definition.
+#define TRUE 1
+#define FALSE 0
+
+
+/* 
+ * Bus Master Data Structures  ----------------------------------------------
  */
 
-struct hp100_rx_header {
-  u_short rx_length;			/* Pkt length is bits 12:0 */
-  u_short rx_status;			/* status of the packet */
-};
+#define MAX_RX_PDL              30   /* Card limit = 31 */
+#define MAX_RX_FRAG             2    /* Dont need more... */
+#define MAX_TX_PDL              29
+#define MAX_TX_FRAG             2   /* Limit = 31 */
+
+/* Define total PDL area size in bytes (should be 4096) */
+/* This is the size of kernel (dma) memory that will be allocated. */
+#define MAX_RINGSIZE ((MAX_RX_FRAG*8+4+4)*MAX_RX_PDL+(MAX_TX_FRAG*8+4+4)*MAX_TX_PDL)+16
+
+/* Ethernet Packet Sizes */
+#define MIN_ETHER_SIZE          60
+#define MAX_ETHER_SIZE          1514    /* Needed for preallocation of */
+                                        /* skb buffer when busmastering */
+
+/* Tx or Rx Ring Entry */
+typedef struct hp100_ring {
+  u_int                     *pdl;       /* Address of PDLs PDH, dword before
+					 * this address is used for rx hdr */
+  u_int                     pdl_paddr;  /* Physical address of PDL */
+  struct sk_buff            *skb;
+  struct hp100_ring         *next;      
+} hp100_ring_t;
+
+
+
+/* Mask for Header Descriptor */
+#define HP100_PKT_LEN_MASK	0x1FFF	/* AND with RxLength to get length */
 
-#define HP100_PKT_LEN_MASK	0x1FFF	/* AND with RxLength to get length bits */
 
 /* Receive Packet Status.  Note, the error bits are only valid if ACC_ERRORED 
    bit in the MAC Configuration Register 1 is set. */
-   
 #define HP100_RX_PRI		0x8000	/* 0:No, 1:Yes packet is priority */
 #define HP100_SDF_ERR		0x4000	/* 0:No, 1:Yes start of frame error */
 #define HP100_SKEW_ERR		0x2000	/* 0:No, 1:Yes skew out of range */
@@ -368,7 +616,11 @@
 	outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW )
 #define hp100_mem_map_disable() \
 	outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW )
-#define hp100_reset_card() \
-	outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
-#define hp100_unreset_card() \
-	outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 2
+ *  tab-width: 8
+ * End:
+*/
diff -urN linux-2.0.31-pre3/drivers/net/Makefile linux-2.0.31-pre4/drivers/net/Makefile
--- linux-2.0.31-pre3/drivers/net/Makefile	1996-10-29 17:42:40.000000000 -0800
+++ linux-2.0.31-pre4/drivers/net/Makefile	2003-08-15 15:03:56.000000000 -0700
@@ -144,6 +144,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_ULTRA32),y)
+L_OBJS += smc-ultra32.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ULTRA),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += smc-ultra32.o
+  endif
+endif
+
 ifeq ($(CONFIG_E2100),y)
 L_OBJS += e2100.o
 CONFIG_8390_BUILTIN = y
diff -urN linux-2.0.31-pre3/drivers/net/smc-ultra32.c linux-2.0.31-pre4/drivers/net/smc-ultra32.c
--- linux-2.0.31-pre3/drivers/net/smc-ultra32.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/net/smc-ultra32.c	2003-08-15 15:03:56.000000000 -0700
@@ -0,0 +1,414 @@
+/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
+
+Sources:
+
+	This driver is based on (cloned from) the ISA SMC Ultra driver
+	written by Donald Becker. Modifications to support the EISA
+	version of the card by Paul Gortmaker and Leonard N. Zubkoff.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+Theory of Operation:
+
+	The SMC Ultra32C card uses the SMC 83c790 chip which is also
+	found on the ISA SMC Ultra cards. It has a shared memory mode of
+	operation that makes it similar to the ISA version of the card.
+	The main difference is that the EISA card has 32KB of RAM, but
+	only an 8KB window into that memory. The EISA card also can be
+	set for a bus-mastering mode of operation via the ECU, but that
+	is not (and probably will never be) supported by this driver.
+	The ECU should be run to enable shared memory and to disable the
+	bus-mastering feature for use with linux.
+
+	By programming the 8390 to use only 8KB RAM, the modifications
+	to the ISA driver can be limited to the probe and initialization
+	code. This allows easy integration of EISA support into the ISA
+	driver. However, the driver development kit from SMC provided the
+	register information for sliding the 8KB window, and hence the 8390
+	is programmed to use the full 32KB RAM.
+
+	Unfortunately this required code changes outside the probe/init
+	routines, and thus we decided to separate the EISA driver from
+	the ISA one. In this way, ISA users don't end up with a larger
+	driver due to the EISA code, and EISA users don't end up with a
+	larger driver due to the ISA EtherEZ PIO code. The driver is
+	similar to the 3c503/16 driver, in that the window must be set
+	back to the 1st 8KB of space for access to the two 8390 Tx slots.
+
+	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
+	be a limiting factor, since the EISA bus could get packets off
+	the card fast enough, but having the use of lots of RAM as Rx
+	space is extra insurance if interrupt latencies become excessive.
+
+*/
+
+static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int ultra32_probe(struct device *dev);
+int ultra32_probe1(struct device *dev, int ioaddr);
+static int ultra32_open(struct device *dev);
+static void ultra32_reset_8390(struct device *dev);
+static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+				 int ring_page);
+static void ultra32_block_input(struct device *dev, int count,
+				struct sk_buff *skb, int ring_offset);
+static void ultra32_block_output(struct device *dev, int count,
+				 const unsigned char *buf, const start_page);
+static int ultra32_close(struct device *dev);
+
+#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
+#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */
+#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */
+#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */
+#define ULTRA32_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
+
+/*
+ * Defines that apply only to the Ultra32 EISA card. Note that
+ * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
+ * into an EISA ID of 0x1080A34D
+ */
+#define ULTRA32_BASE	0xca0
+#define ULTRA32_ID	0x1080a34d
+#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */
+/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
+#define ULTRA32_CFG1	0x04	/* 0xca4 */
+#define ULTRA32_CFG2	0x05	/* 0xca5 */
+#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */
+#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */
+#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */
+#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */
+#define ULTRA32_CFG7	0x0d	/* 0xcad */
+
+
+/*	Probe for the Ultra32.  This looks like a 8013 with the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following.
+*/
+
+int ultra32_probe(struct device *dev)
+{
+	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+	int ioaddr, edge, media;
+
+	if (!EISA_bus) return ENODEV;
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000)
+	if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 &&
+	    inb(ioaddr + ULTRA32_IDPORT) != 0xff &&
+	    inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) {
+		media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+		edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+		printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+		       ioaddr >> 12, ifmap[media],
+		       (edge ? "Edge Triggered" : "Level Sensitive"));
+		if (ultra32_probe1(dev, ioaddr) == 0)
+		  return 0;
+	}
+	return ENODEV;
+}
+
+int ultra32_probe1(struct device *dev, int ioaddr)
+{
+	int i;
+	int checksum = 0;
+	const char *model_name;
+	static unsigned version_printed = 0;
+	/* Values from various config regs. */
+	unsigned char idreg = inb(ioaddr + 7);
+	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+	/* Check the ID nibble. */
+	if ((idreg & 0xf0) != 0x20) 			/* SMC Ultra */
+		return ENODEV;
+
+	/* Select the station address register set. */
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xff)
+		return ENODEV;
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("smc-ultra32.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	model_name = "SMC Ultra32";
+
+	printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr);
+
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+	/* Switch from the station address to the alternate register set and
+	   read the useful registers there. */
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+	/* Reset RAM addr. */
+	outb(0x00, ioaddr + 0x0b);
+
+	/* Switch back to the station address register set so that the
+	   MS-DOS driver can find the card after a warm boot. */
+	outb(reg4, ioaddr + 4);
+
+	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
+		printk("\nsmc-ultra32: Card RAM is disabled!  "
+		       "Run EISA config utility.\n");
+		return ENODEV;
+	}
+	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
+		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  "
+		       "Run EISA config utility.\n");
+
+	if (dev->irq < 2) {
+		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
+		if (irq == 0) {
+			printk(", failed to detect IRQ line.\n");
+			return -EAGAIN;
+		}
+		dev->irq = irq;
+	}
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (", no memory for dev->priv.\n");
+                return -ENOMEM;
+        }
+
+	/* OK, we are certain this is going to work.  Setup the device. */
+	request_region(ioaddr, ULTRA32_IO_EXTENT, model_name);
+
+	/* The 8390 isn't at the base address, so fake the offset */
+	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
+
+	/* Save RAM address in the unused reg0 to avoid excess inb's. */
+	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
+
+	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
+
+	ei_status.name = model_name;
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = 0;
+	ei_status.rx_start_page = TX_PAGES;
+	/* All Ultra32 cards have 32KB memory with an 8KB window. */
+	ei_status.stop_page = 128;
+
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = dev->rmem_end = dev->mem_start + 0x1fff;
+
+	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
+	       dev->irq, dev->mem_start, dev->mem_end);
+	ei_status.block_input = &ultra32_block_input;
+	ei_status.block_output = &ultra32_block_output;
+	ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
+	ei_status.reset_8390 = &ultra32_reset_8390;
+	dev->open = &ultra32_open;
+	dev->stop = &ultra32_close;
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static int ultra32_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+		return -EAGAIN;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ultra32_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
+	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
+	free_irq(dev->irq, NULL);
+	irq2dev_map[dev->irq] = 0;
+
+	NS8390_init(dev, 0);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static void ultra32_reset_8390(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
+
+	outb(ULTRA32_RESET, ioaddr);
+	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	if (ei_debug > 1) printk("reset done\n");
+	return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ultra32_get_8390_hdr(struct device *dev,
+				 struct e8390_pkt_hdr *hdr,
+				 int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page & 0x1f) << 8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select correct 8KB Window. */
+	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps, or in this case, when a
+   packet spans an 8KB boundary. Note that the current 8KB segment is
+   already set by the get_8390_hdr routine. */
+
+static void ultra32_block_input(struct device *dev,
+				int count,
+				struct sk_buff *skb,
+				int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + (ring_offset & 0x1fff);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
+		int semi_count = 8192 - (ring_offset & 0x1FFF);
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		if (ring_offset < 96*256) {
+			/* Select next 8KB Window. */
+			ring_offset += semi_count;
+			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->mem_start, count);
+		} else {
+			/* Select first 8KB Window. */
+			outb(ei_status.reg0, RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+		}
+	} else {
+		/* Packet is in one chunk -- we can copy + cksum. */
+		eth_io_copy_and_sum(skb, xfer_start, count, 0);
+	}
+}
+
+static void ultra32_block_output(struct device *dev,
+				 int count,
+				 const unsigned char *buf,
+				 int start_page)
+{
+	unsigned long xfer_start = dev->mem_start + (start_page<<8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select first 8KB Window. */
+	outb(ei_status.reg0, RamReg);
+
+	memcpy_toio(xfer_start, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */
+#define NAMELEN		    8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA32_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA32_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->init = ultra32_probe;
+		if (register_netdev(dev) != 0) {
+			if (found > 0) return 0; /* Got at least one. */
+			printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
+			return -ENXIO;
+		}
+		found++;
+	}
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: ultra32_close_card() does free_irq + irq2dev */
+			int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, ULTRA32_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff -urN linux-2.0.31-pre3/drivers/net/Space.c linux-2.0.31-pre4/drivers/net/Space.c
--- linux-2.0.31-pre3/drivers/net/Space.c	1997-04-08 08:47:45.000000000 -0700
+++ linux-2.0.31-pre4/drivers/net/Space.c	2003-08-15 15:03:56.000000000 -0700
@@ -41,6 +41,7 @@
 extern int tulip_probe(struct device *dev);
 extern int hp100_probe(struct device *dev);
 extern int ultra_probe(struct device *dev);
+extern int ultra32_probe(struct device *dev);
 extern int wd_probe(struct device *dev);
 extern int el2_probe(struct device *dev);
 extern int ne_probe(struct device *dev);
@@ -111,6 +112,9 @@
 #if defined(CONFIG_ULTRA)
 	&& ultra_probe(dev)
 #endif
+#if defined(CONFIG_ULTRA32)
+	&& ultra32_probe(dev)
+#endif
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
 #endif
diff -urN linux-2.0.31-pre3/drivers/net/tulip.c linux-2.0.31-pre4/drivers/net/tulip.c
--- linux-2.0.31-pre3/drivers/net/tulip.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/net/tulip.c	2003-08-15 15:03:56.000000000 -0700
@@ -499,7 +499,7 @@
 #if defined (MODULE)
 	return cards_found;
 #else
-	return 0;
+	return cards_found > 0 ? 0 : -ENODEV;
 #endif
 }
 
diff -urN linux-2.0.31-pre3/drivers/pci/pci.c linux-2.0.31-pre4/drivers/pci/pci.c
--- linux-2.0.31-pre3/drivers/pci/pci.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/pci/pci.c	2003-08-15 15:03:56.000000000 -0700
@@ -105,6 +105,7 @@
 	DEVICE( SI,		SI_5511,		"85C5511"),
 	DEVICE( SI,		SI_5513,		"85C5513"),
 	DEVICE( HP,		HP_J2585A,	"J2585A"),
+	DEVICE( HP,		HP_J2585B,	"J2585B (Lassen)"),
 	DEVICE( PCTECH,		PCTECH_RZ1000,  "RZ1000 (buggy)"),
 	DEVICE( PCTECH,		PCTECH_RZ1001,  "RZ1001 (buggy?)"),
 	DEVICE( DPT,		DPT,		"SmartCache/Raid"),
@@ -200,6 +201,7 @@
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
+	DEVICE( COMPEX,		COMPEX_ENET100VG4, "Readylink ENET100-VG4"),
 	DEVICE( COMPEX,		COMPEX_RL2000,	"ReadyLink 2000"),
 	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
 	DEVICE( RP,             RP8INTF,        "RocketPort 8 Intf"),
diff -urN linux-2.0.31-pre3/drivers/scsi/aic7xxx_proc.c linux-2.0.31-pre4/drivers/scsi/aic7xxx_proc.c
--- linux-2.0.31-pre3/drivers/scsi/aic7xxx_proc.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/aic7xxx_proc.c	2003-08-15 15:03:56.000000000 -0700
@@ -76,7 +76,6 @@
 {
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
-  static u8 buff[512];
   int   i;
   int   found = FALSE;
   int   size = 0;
@@ -129,11 +128,6 @@
     return (aic7xxx_set_info(buffer, length, HBAptr));
   }
 
-  if (offset == 0)
-  {
-    memset(buff, 0, sizeof(buff));
-  }
-
   p = (struct aic7xxx_host *) HBAptr->hostdata;
 
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
@@ -142,7 +136,14 @@
 #if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
 #endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
@@ -167,7 +168,16 @@
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
@@ -201,7 +211,16 @@
       (p->flags & ULTRA_ENABLED) ? "En" : "Dis");
   size += sprintf(BLS, "     Target Disconnect: %sabled\n",
       p->discenable ? "En" : "Dis");
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
 #ifdef AIC7XXX_PROC_STATS
   {
@@ -210,6 +229,7 @@
 
     /*
      * XXX: Need to fix this to avoid overflow...
+     * Fixed - gordo.
      */
     size += sprintf(BLS, "\n");
     size += sprintf(BLS, "Statistics:\n");
@@ -247,9 +267,18 @@
               sp->w_bins[9]);
           size += sprintf(BLS, "\n");
         }
+	if (size > 512)
+	  printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
+        len += size; pos = begin + len; size = 0;
+	if (pos < offset)
+	{
+	  begin = pos;
+	  len = 0;
+	}
+	else if (pos >= offset + length)
+	  goto stop_output;
       }
     }
-    len += size; pos = begin + len; size = 0;
   }
 #endif /* AIC7XXX_PROC_STATS */
 
@@ -257,7 +286,11 @@
   proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
   *start = buffer + (offset - begin);   /* Start of wanted data */
   len -= (offset - begin);      /* Start slop */
-  if (len > length)
+  if (len < 0)
+  {
+    len = 0;			/* off end of file */
+  }
+  else if (len > length)
   {
     len = length;               /* Ending slop */
   }
diff -urN linux-2.0.31-pre3/drivers/scsi/BusLogic.c linux-2.0.31-pre4/drivers/scsi/BusLogic.c
--- linux-2.0.31-pre3/drivers/scsi/BusLogic.c	1997-03-29 09:01:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/BusLogic.c	2003-08-15 15:03:56.000000000 -0700
@@ -27,8 +27,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.9"
-#define BusLogic_DriverDate		"29 March 1997"
+#define BusLogic_DriverVersion		"2.0.10"
+#define BusLogic_DriverDate		"11 August 1997"
 
 
 #include <linux/module.h>
@@ -505,13 +505,14 @@
     Wait for the Host Adapter Ready bit to be set and the Command/Parameter
     Register Busy bit to be reset in the Status Register.
   */
-  TimeoutCounter = loops_per_sec >> 3;
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.HostAdapterReady &&
 	  !StatusRegister.Bits.CommandParameterRegisterBusy)
 	break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -587,11 +588,11 @@
     case BusLogic_InquireInstalledDevicesID8to15:
     case BusLogic_InquireTargetDevices:
       /* Approximately 60 seconds. */
-      TimeoutCounter = loops_per_sec << 2;
+      TimeoutCounter = 60*10000;
       break;
     default:
       /* Approximately 1 second. */
-      TimeoutCounter = loops_per_sec >> 4;
+      TimeoutCounter = 10000;
       break;
     }
   /*
@@ -611,6 +612,7 @@
 	else BusLogic_ReadDataInRegister(HostAdapter);
       if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
 	  StatusRegister.Bits.HostAdapterReady) break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -786,7 +788,7 @@
   boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount];
   BusLogic_ProbeInfo_T *PrimaryProbeInfo =
     &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
-  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount;
+  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
   int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
   boolean ForceBusDeviceScanningOrder = false;
   boolean ForceBusDeviceScanningOrderChecked = false;
@@ -1240,13 +1242,16 @@
     {
       FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *)
 	scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC);
+      int Retries = 10;
       if (FlashPointInfo == NULL)
 	return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO");
       FlashPointInfo->BaseAddress = HostAdapter->IO_Address;
       FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
       FlashPointInfo->Present = false;
-      if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
-	    FlashPointInfo->Present))
+      while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+	       FlashPointInfo->Present) &&
+	     --Retries >= 0) ;
+      if (!FlashPointInfo->Present)
 	{
 	  scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
 	  BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
@@ -1321,7 +1326,7 @@
 					     *HostAdapter)
 {
   BusLogic_StatusRegister_T StatusRegister;
-  int TimeoutCounter = loops_per_sec;
+  int TimeoutCounter;
   /*
     FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
   */
@@ -1344,10 +1349,12 @@
   /*
     Wait until Diagnostic Active is set in the Status Register.
   */
+  TimeoutCounter = 5*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, "
@@ -1363,10 +1370,12 @@
   /*
     Wait until Diagnostic Active is reset in the Status Register.
   */
+  TimeoutCounter = 10*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (!StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, "
@@ -1377,6 +1386,7 @@
     Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
     or Data In Register Ready bits is set in the Status Register.
   */
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
@@ -1384,6 +1394,7 @@
 	  StatusRegister.Bits.HostAdapterReady ||
 	  StatusRegister.Bits.DataInRegisterReady)
 	break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, "
@@ -2998,8 +3009,6 @@
 			     "Incoming Mailbox\n", HostAdapter,
 			     CCB->SerialNumber, CCB->Status);
 	  }
-      else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n",
-			    HostAdapter, CCB->SerialNumber, CCB->TargetID);
       NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
       if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
 	NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
diff -urN linux-2.0.31-pre3/drivers/scsi/BusLogic.h linux-2.0.31-pre4/drivers/scsi/BusLogic.h
--- linux-2.0.31-pre3/drivers/scsi/BusLogic.h	1997-03-29 09:01:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/BusLogic.h	2003-08-15 15:03:56.000000000 -0700
@@ -27,6 +27,9 @@
 */
 
 
+#include <linux/config.h>
+
+
 /*
   Define types for some of the structures that interface with the rest
   of the Linux Kernel and SCSI Subsystem.
diff -urN linux-2.0.31-pre3/drivers/scsi/eata.c linux-2.0.31-pre4/drivers/scsi/eata.c
--- linux-2.0.31-pre3/drivers/scsi/eata.c	1997-02-26 10:56:51.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/eata.c	2003-08-15 15:03:56.000000000 -0700
@@ -1,6 +1,13 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ *          Use of serial_number_at_timeout in abort and reset processing.
+ *          Use of the __initfunc and __initdata macro in setup code.
+ *          Minor cleanups in the list_statistics code.
+ *          Increased controller busy timeout in order to better support 
+ *          slow SCSI devices.
+ *
  *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
  *          When loading as a module, parameter passing is now supported
  *          both in 2.0 and in 2.1 style.
@@ -202,8 +209,7 @@
  *  lc:n  disables linked commands;
  *  tc:y  enables tagged commands;
  *  tc:n  disables tagged commands;
- *  tm:0  use head/simple/ordered queue tag sequences for reads and ordered
- *        queue tags for writes;
+ *  tm:0  use head/simple/ordered queue tag sequences;
  *  tm:1  use only simple queue tags;
  *  tm:2  use only head of queue tags;
  *  tm:3  use only ordered queue tags;
@@ -232,10 +238,12 @@
  *  between increasing or decreasing by minimizing the seek distance between
  *  the sector of the commands just completed and the sector of the first 
  *  command in the list to be sorted. 
- *  Trivial math assures that if there are (Q-1) outstanding request for
- *  random seeks over S sectors, the unsorted average seek distance is S/2,
- *  while the sorted average seek distance is S/(Q-1). The seek distance is
- *  hence divided by a factor (Q-1)/2.
+ *  Trivial math assures that the unsorted average seek distance when doing
+ *  random seeks over S sectors is S/3.
+ *  When (Q-1) requests are uniformly distributed over S sectors, the average
+ *  distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ *  average seek distance for (Q-1) random requests over S sectors is S/Q.
+ *  The elevator sorting hence divides the seek distance by a factor Q/3.
  *  The above pure geometric remarks are valid in all cases and the 
  *  driver effectively reduces the seek distance by the predicted factor
  *  when there are Q concurrent read i/o operations on the device, but this
@@ -286,10 +294,18 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 #include "eata.h"
-#include<linux/stat.h>
-#include<linux/config.h>
-#include<linux/bios32.h>
-#include<linux/pci.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
 
 struct proc_dir_entry proc_scsi_eata2x = {
     PROC_SCSI_EATA2X, 6, "eata2x",
@@ -517,7 +533,7 @@
 static const char *driver_name = "EATA";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = { 
+static unsigned int io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -638,8 +654,7 @@
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase) {
-   unsigned int loop = MAXLOOP;
+static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
 
    while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED)
       if (--loop == 0) return TRUE;
@@ -649,7 +664,7 @@
 
 static inline int do_dma(unsigned int iobase, unsigned int addr, unchar cmd) {
 
-   if (wait_on_busy(iobase)) return TRUE;
+   if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE;
 
    if ((addr = V2DEV(addr))) {
       outb((char) (addr >> 24), iobase + REG_LOW);
@@ -678,8 +693,8 @@
    return FALSE;
 }
 
-static inline int port_detect(unsigned int port_base, unsigned int j, 
-			      Scsi_Host_Template *tpnt) {
+__initfunc (static inline int port_detect \
+      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
@@ -882,7 +897,7 @@
          sh[j]->max_lun = info.max_lun + 1;
       }
 
-   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
    else                       sprintf(dma_name, "DMA %u", dma_channel);
 
    for (i = 0; i < sh[j]->can_queue; i++)
@@ -941,7 +956,7 @@
    return TRUE;
 }
 
-void eata2x_setup(char *str, int *ints) {
+__initfunc (void eata2x_setup(char *str, int *ints)) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -974,7 +989,7 @@
    return;
 }
 
-static void add_pci_ports(void) {
+__initfunc (static void add_pci_ports(void)) {
 
 #if defined(CONFIG_PCI)
 
@@ -1009,7 +1024,7 @@
    return;
 }
 
-int eata2x_detect(Scsi_Host_Template *tpnt) {
+__initfunc (int eata2x_detect(Scsi_Host_Template *tpnt)) {
    unsigned long flags;
    unsigned int j = 0, k;
 
@@ -1166,6 +1181,9 @@
 
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
+#if 0
+   if (SCpnt->device->type == TYPE_TAPE) cpp->hbaci = TRUE;
+#endif
    cpp->one = TRUE;
    cpp->channel = SCpnt->channel;
    cpp->target = SCpnt->target;
@@ -1182,14 +1200,12 @@
       else if (tag_mode == TAG_SIMPLE)  cpp->mess[0] = SIMPLE_QUEUE_TAG;
       else if (tag_mode == TAG_HEAD)    cpp->mess[0] = HEAD_OF_QUEUE_TAG;
       else if (tag_mode == TAG_ORDERED) cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 0)
+      else if (SCpnt->device->current_tag == 0)
          cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 1)
+      else if (SCpnt->device->current_tag == 1)
          cpp->mess[0] = HEAD_OF_QUEUE_TAG;
-      else if (cpp->din)
-         cpp->mess[0] = SIMPLE_QUEUE_TAG;
       else
-         cpp->mess[0] = ORDERED_QUEUE_TAG;
+         cpp->mess[0] = SIMPLE_QUEUE_TAG;
 
       cpp->mess[1] = SCpnt->device->current_tag++;
       }
@@ -1208,7 +1224,7 @@
    if (linked_comm && SCpnt->device->queue_depth > 2
                                      && TLDEV(SCpnt->device->type)) {
       HD(j)->cp_stat[i] = READY;
-      flush_dev(SCpnt->device, 0, j, FALSE);
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
       restore_flags(flags);
       return 0;
       }
@@ -1238,7 +1254,8 @@
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
 
-   if (SCarg->host_scribble == NULL) {
+   if (SCarg->host_scribble == NULL
+       || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
 	     BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
@@ -1252,7 +1269,7 @@
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: abort, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_ABORT_ERROR;
@@ -1321,13 +1338,19 @@
    if (SCarg->host_scribble == NULL)
       printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
 
+   if (SCarg->serial_number != SCarg->serial_number_at_timeout) {
+      printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+      restore_flags(flags);
+      return SCSI_RESET_NOT_RUNNING;
+      }
+
    if (HD(j)->in_reset) {
       printk("%s: reset, exit, already in reset.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, exit, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1480,7 +1503,7 @@
    unsigned int rev = FALSE, s = TRUE, r = TRUE;
    unsigned int input_only = TRUE, overlap = FALSE;
    unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
-   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
 
    static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
    static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
@@ -1491,8 +1514,8 @@
       printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
              " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
              ovlcount, readycount, readysorted, sortcount, revcount,
-             seeknosort / (readycount - batchcount + 1), 
-             seeksorted / (readycount - batchcount + 1));
+             seeknosort / (readycount + 1), 
+             seeksorted / (readycount + 1));
 
    if (n_ready <= 1) return;
 
@@ -1520,6 +1543,10 @@
 
       }
 
+   if (link_statistics) {
+      if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+      }
+
    if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
 
    if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
@@ -1537,10 +1564,11 @@
    if (overlap) sort(pl, il, n_ready, FALSE);
 
    if (link_statistics) {
+      if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
       batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
       if (input_only) inputcount++;
       if (overlap) { ovlcount++; seeksorted += seek / 1024; }
-      else seeksorted += (maxsec - minsec) / 1024;
+      else seeksorted += (iseek + maxsec - minsec) / 1024;
       if (rev && !r)     {  revcount++; readysorted += n_ready; }
       if (!rev && !s)    { sortcount++; readysorted += n_ready; }
       }
diff -urN linux-2.0.31-pre3/drivers/scsi/eata.h linux-2.0.31-pre4/drivers/scsi/eata.h
--- linux-2.0.31-pre3/drivers/scsi/eata.h	1997-02-26 10:56:51.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/eata.h	2003-08-15 15:03:56.000000000 -0700
@@ -12,7 +12,7 @@
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "3.00.09"
+#define EATA_VERSION "3.10.00"
 
 
 #define EATA {                                                 \
diff -urN linux-2.0.31-pre3/drivers/scsi/eata_pio_proc.c linux-2.0.31-pre4/drivers/scsi/eata_pio_proc.c
--- linux-2.0.31-pre3/drivers/scsi/eata_pio_proc.c	1996-04-11 23:49:41.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/eata_pio_proc.c	2003-08-15 15:03:56.000000000 -0700
@@ -1,19 +1,4 @@
 
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const pio_scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 /*
  * eata_set_info
  * buffer : pointer to the data that has been written to the hostfile
diff -urN linux-2.0.31-pre3/drivers/scsi/FlashPoint.c linux-2.0.31-pre4/drivers/scsi/FlashPoint.c
--- linux-2.0.31-pre3/drivers/scsi/FlashPoint.c	1997-03-29 09:01:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/FlashPoint.c	2003-08-15 15:03:56.000000000 -0700
@@ -31,6 +31,18 @@
 #endif
 
 
+/*
+  FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#endif
+
+
 #ifndef CONFIG_SCSI_OMIT_FLASHPOINT
 
 
diff -urN linux-2.0.31-pre3/drivers/scsi/g_NCR5380.c linux-2.0.31-pre4/drivers/scsi/g_NCR5380.c
--- linux-2.0.31-pre3/drivers/scsi/g_NCR5380.c	1996-11-22 02:06:30.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/g_NCR5380.c	2003-08-15 15:03:56.000000000 -0700
@@ -606,21 +606,6 @@
     return len-start;
 }
 
-const char *const private_scsi_device_types[] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-#define MAX_SCSI_DEVICE_CODE sizeof(private_scsi_device_types)/sizeof(char*)
-
 int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
 {
     int len = 0;
@@ -628,7 +613,6 @@
     unsigned char status;
     int i;
     struct Scsi_Host *scsi_ptr;
-	Scsi_Device *dev;
     Scsi_Cmnd *ptr;
     struct NCR5380_hostdata *hostdata;
 
@@ -674,7 +658,7 @@
 	    long tr = hostdata->time_read[dev->id] / HZ;
 	    long tw = hostdata->time_write[dev->id] / HZ;
 
-	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? private_scsi_device_types[(int)dev->type] : "Unknown");
+	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown");
 	    for (i=0; i<8; i++)
 		if (dev->vendor[i] >= 0x20)
 		    *(buffer+(len++)) = dev->vendor[i];
diff -urN linux-2.0.31-pre3/drivers/scsi/in2000.c linux-2.0.31-pre4/drivers/scsi/in2000.c
--- linux-2.0.31-pre3/drivers/scsi/in2000.c	1996-05-09 06:40:24.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/in2000.c	2003-08-15 15:03:56.000000000 -0700
@@ -104,7 +104,7 @@
  *
  */
 
-
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <linux/sched.h>
@@ -115,49 +115,44 @@
 #include <linux/ioport.h>
 #include <linux/blkdev.h>
 
-#include "scsi.h"
-#include "sd.h"
-#include "hosts.h"
-#include "in2000.h"
-
 #include <linux/blk.h>
 #include <linux/stat.h>
 
-#ifdef MODULE
-#include <linux/module.h>
-#endif
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
 
 
-#define IN2000_VERSION    "1.28"
-#define IN2000_DATE       "07/May/1996"
+#define IN2000_VERSION    "1.31"
+#define IN2000_DATE       "06/July/1997"
 
-#define PROC_INTERFACE     /* add code for /proc/scsi/in2000/xxx interface */
-#define SYNC_DEBUG         /* extra info on sync negotiation printed */
-#define DEBUGGING_ON       /* enable command-line debugging bitmask */
-#define DEBUG_DEFAULTS 0   /* default bitmask - change from command-line */
+/*
+ * Note - the following defines have been moved to 'in2000.h':
+ *
+ *    PROC_INTERFACE
+ *    PROC_STATISTICS
+ *    SYNC_DEBUG
+ *    DEBUGGING_ON
+ *    DEBUG_DEFAULTS
+ *    FAST_READ_IO
+ *    FAST_WRITE_IO
+ *
+ */
 
-#define FAST_READ_IO       /* No problems with these on my machine */
-#define FAST_WRITE_IO
 
-#ifdef DEBUGGING_ON
-#define DB(f,a) if (hostdata->args & (f)) a;
-#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
-#else
-#define DB(f,a)
-#define CHECK_NULL(p,s)
-#endif
+#include "in2000.h"
 
-#define IS_DIR_OUT(cmd) ((cmd)->cmnd[0] == WRITE_6  || \
-                         (cmd)->cmnd[0] == WRITE_10 || \
-                         (cmd)->cmnd[0] == WRITE_12)
 
 
 /*
- * setup_strings is an array of strings that define some of the operating
- * parameters and settings for this driver. It is used unless a LILO
- * or insmod command line has been specified, in which case those settings
- * are combined with the ones here. The driver recognizes the following
- * keywords (lower case required) and arguments:
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with a LILO or insmod command-line, those
+ * settings are combined with 'setup_args[]'. Note that LILO command-lines
+ * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
+ * The driver recognizes the following keywords (lower case required) and
+ * arguments:
  *
  * -  ioport:addr    -Where addr is IO address of a (usually ROM-less) card.
  * -  noreset        -No optional args. Prevents SCSI bus reset at boot time.
@@ -182,13 +177,11 @@
  *    _must_ be a colon between a keyword and its numeric argument, with no
  *    spaces.
  * -  Keywords are separated by commas, no spaces, in the standard kernel
- *    command-line manner, except in the case of 'setup_strings[]' (see
- *    below), which is simply a C array of pointers to char. Each element
- *    in the array is a string comprising one keyword & argument.
+ *    command-line manner.
  * -  A keyword in the 'nth' comma-separated command-line member will overwrite
- *    the 'nth' element of setup_strings[]. A blank command-line member (in
+ *    the 'nth' element of setup_args[]. A blank command-line member (in
  *    other words, a comma with no preceding keyword) will _not_ overwrite
- *    the corresponding setup_strings[] element.
+ *    the corresponding setup_args[] element.
  *
  * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
  * -  in2000=ioport:0x220,noreset
@@ -197,57 +190,23 @@
  * -  in2000=proc:3
  */
 
-static char *setup_strings[] =
-      {"","","","","","","","","","","",""};
+/* Normally, no defaults are specified... */
+static char *setup_args[] =
+      {"","","","","","","","",""};
 
-static struct Scsi_Host *instance_list = 0;
+/* filled in by 'insmod' */
+static char *setup_strings = 0;
 
-#ifdef PROC_INTERFACE
-unsigned long disc_allowed_total;
-unsigned long disc_taken_total;
+#ifdef MODULE_PARM
+MODULE_PARM(setup_strings, "s");
 #endif
 
 
-#define read1_io(a)     (inb(hostdata->io_base+(a)))
-#define read2_io(a)     (inw(hostdata->io_base+(a)))
-#define write1_io(b,a)  (outb((b),hostdata->io_base+(a)))
-#define write2_io(w,a)  (outw((w),hostdata->io_base+(a)))
-
-/* These inline assembly defines are derived from a patch
- * sent to me by Bill Earnest. He's done a lot of very
- * valuable thinking, testing, and coding during his effort
- * to squeeze more speed out of this driver. I really think
- * that we are doing IO at close to the maximum now with
- * the fifo. (And yes, insw uses 'edi' while outsw uses
- * 'esi'. Thanks Bill!)
- */
-
-#define FAST_READ2_IO()    \
-   __asm__ __volatile__ ("\n \
-   cld                    \n \
-   orl %%ecx, %%ecx       \n \
-   jz 1f                  \n \
-   rep                    \n \
-   insw %%dx              \n \
-1: "                       \
-   : "=D" (sp)                   /* output */   \
-   : "d" (f), "D" (sp), "c" (i)  /* input */    \
-   : "edx", "ecx", "edi" )       /* trashed */
-
-#define FAST_WRITE2_IO()   \
-   __asm__ __volatile__ ("\n \
-   cld                    \n \
-   orl %%ecx, %%ecx       \n \
-   jz 1f                  \n \
-   rep                    \n \
-   outsw %%dx             \n \
-1: "                       \
-   : "=S" (sp)                   /* output */   \
-   : "d" (f), "S" (sp), "c" (i)  /* input */    \
-   : "edx", "ecx", "esi" )       /* trashed */
+static struct Scsi_Host *instance_list = 0;
+
 
 
-inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
+static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
 {
    write1_io(reg_num,IO_WD_ADDR);
    return read1_io(IO_WD_DATA);
@@ -257,14 +216,14 @@
 #define READ_AUX_STAT() read1_io(IO_WD_ASR)
 
 
-inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
+static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
 {
    write1_io(reg_num,IO_WD_ADDR);
    write1_io(value,IO_WD_DATA);
 }
 
 
-inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
+static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
 {
 /*   while (READ_AUX_STAT() & ASR_CIP)
       printk("|");*/
@@ -273,7 +232,7 @@
 }
 
 
-uchar read_1_byte(struct IN2000_hostdata *hostdata)
+static uchar read_1_byte(struct IN2000_hostdata *hostdata)
 {
 uchar asr, x = 0;
 
@@ -288,7 +247,7 @@
 }
 
 
-void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
+static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
 {
    write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
    write1_io((value >> 16),IO_WD_DATA);
@@ -297,7 +256,7 @@
 }
 
 
-unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
+static unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
 {
 unsigned long value;
 
@@ -309,6 +268,32 @@
 }
 
 
+/* The 33c93 needs to be told which direction a command transfers its
+ * data; we use this function to figure it out. Returns true if there
+ * will be a DATA_OUT phase with this command, false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ */
+static int is_dir_out(Scsi_Cmnd *cmd)
+{
+   switch (cmd->cmnd[0]) {
+      case WRITE_6:           case WRITE_10:          case WRITE_12:
+      case WRITE_LONG:        case WRITE_SAME:        case WRITE_BUFFER:
+      case WRITE_VERIFY:      case WRITE_VERIFY_12:      
+      case COMPARE:           case COPY:              case COPY_VERIFY:
+      case SEARCH_EQUAL:      case SEARCH_HIGH:       case SEARCH_LOW:
+      case SEARCH_EQUAL_12:   case SEARCH_HIGH_12:    case SEARCH_LOW_12:      
+      case FORMAT_UNIT:       case REASSIGN_BLOCKS:   case RESERVE:
+      case MODE_SELECT:       case MODE_SELECT_10:    case LOG_SELECT:
+      case SEND_DIAGNOSTIC:   case CHANGE_DEFINITION: case UPDATE_BLOCK:
+      case SET_WINDOW:        case MEDIUM_SCAN:       case SEND_VOLUME_TAG:
+      case 0xea:
+         return 1;
+      default:
+         return 0;
+      }
+}
+
+
 
 static struct sx_period sx_table[] = {
    {  1, 0x20},
@@ -321,7 +306,7 @@
    {1000,0x00},
    {0,   0} };
 
-int round_period(unsigned int period)
+static int round_period(unsigned int period)
 {
 int x;
 
@@ -334,7 +319,7 @@
    return 7;
 }
 
-uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+static uchar calc_sync_xfer(unsigned int period, unsigned int offset)
 {
 uchar result;
 
@@ -346,7 +331,7 @@
 
 
 
-void in2000_execute(struct Scsi_Host *instance);
+static void in2000_execute(struct Scsi_Host *instance);
 
 int in2000_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
@@ -354,7 +339,6 @@
 Scsi_Cmnd *tmp;
 unsigned long flags;
 
-
    hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
 
 DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
@@ -372,7 +356,7 @@
 /* We use the Scsi_Pointer structure that's included with each command
  * as a scratchpad (as it's intended to be used!). The handy thing about
  * the SCp.xxx fields is that they're always associated with a given
- * cmd, and are preserved across disconnect-reconnect. This means we
+ * cmd, and are preserved across disconnect-reselect. This means we
  * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
  * if we keep all the critical pointers and counters in SCp:
  *  - SCp.ptr is the pointer into the RAM buffer
@@ -400,9 +384,24 @@
 
 /* We don't set SCp.phase here - that's done in in2000_execute() */
 
-/* Preset the command status to GOOD, since that's the normal case */
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
 
-   cmd->SCp.Status = GOOD;
+   cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
 
    save_flags(flags);
    cli();
@@ -444,7 +443,7 @@
  * the input_Q, using the first command we find that's intended
  * for a currently non-busy target/lun.
  */
-void in2000_execute (struct Scsi_Host *instance)
+static void in2000_execute (struct Scsi_Host *instance)
 {
 struct IN2000_hostdata *hostdata;
 Scsi_Cmnd *cmd, *prev;
@@ -500,11 +499,15 @@
    else
       hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
 
+#ifdef PROC_STATISTICS
+   hostdata->cmd_cnt[cmd->target]++;
+#endif
+
 /*
  * Start the selection process
  */
 
-   if (IS_DIR_OUT(cmd))
+   if (is_dir_out(cmd))
       write_3393(hostdata,WD_DESTINATION_ID, cmd->target);
    else
       write_3393(hostdata,WD_DESTINATION_ID, cmd->target | DSTID_DPD);
@@ -557,8 +560,8 @@
 yes:
    cmd->SCp.phase = 1;
 
-#ifdef PROC_INTERFACE
-   disc_allowed_total++;
+#ifdef PROC_STATISTICS
+   hostdata->disc_allowed_cnt[cmd->target]++;
 #endif
 
 no:
@@ -654,7 +657,7 @@
          write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
          write1_io(0, IO_FIFO_WRITE);  /* clear fifo counter, write mode */
 
-         if (IS_DIR_OUT(cmd)) {
+         if (is_dir_out(cmd)) {
             hostdata->fifo = FI_FIFO_WRITING;
             if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) )
                i = IN2000_FIFO_SIZE - 16;
@@ -718,7 +721,7 @@
 
 
 
-void transfer_pio(uchar *buf, int cnt,
+static void transfer_pio(uchar *buf, int cnt,
                   int data_in_dir, struct IN2000_hostdata *hostdata)
 {
 uchar asr;
@@ -754,7 +757,7 @@
 
 
 
-void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
+static void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
 {
 struct IN2000_hostdata *hostdata;
 unsigned short *sp;
@@ -791,7 +794,8 @@
 
    if (data_in_dir) {
       write1_io(0,IO_FIFO_READ);
-      if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+      if ((hostdata->level2 >= L2_DATA) ||
+          (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
          write_3393(hostdata,WD_COMMAND_PHASE,0x45);
          write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
          hostdata->state = S_RUNNING_LEVEL2;
@@ -808,7 +812,8 @@
  * write any bytes that don't make it at this stage.
  */
 
-   if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+      if ((hostdata->level2 >= L2_DATA) ||
+          (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
       write_3393(hostdata,WD_COMMAND_PHASE,0x45);
       write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
       hostdata->state = S_RUNNING_LEVEL2;
@@ -847,7 +852,7 @@
  * re-think the multiple card capability....
  */
 
-void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
+static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
 {
 struct Scsi_Host *instance;
 struct IN2000_hostdata *hostdata;
@@ -874,6 +879,10 @@
    save_flags(flags);
    sti();
 
+#ifdef PROC_STATISTICS
+   hostdata->int_cnt++;
+#endif
+
 /* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
  * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
  * with a big logic array, so it's a little different than what you might
@@ -1181,9 +1190,10 @@
       case CSR_XFER_DONE|PHS_STATUS:
       case CSR_UNEXP    |PHS_STATUS:
       case CSR_SRV_REQ  |PHS_STATUS:
-DB(DB_INTR,printk("STATUS"))
+DB(DB_INTR,printk("STATUS="))
 
          cmd->SCp.Status = read_1_byte(hostdata);
+DB(DB_INTR,printk("%02x",cmd->SCp.Status))
          if (hostdata->level2 >= L2_BASIC) {
             sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
             hostdata->state = S_RUNNING_LEVEL2;
@@ -1191,7 +1201,6 @@
             write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
             }
          else {
-DB(DB_INTR,printk("=%02x",cmd->SCp.Status))
             hostdata->state = S_CONNECTED;
             }
          break;
@@ -1362,15 +1371,16 @@
 DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
             cmd->SCp.Message = COMMAND_COMPLETE;
             lun = read_3393(hostdata,WD_TARGET_LUN);
-            if (cmd->SCp.Status == GOOD)
-               cmd->SCp.Status = lun;
+DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun))
             hostdata->connected = NULL;
-            if (cmd->cmnd[0] != REQUEST_SENSE)
-               cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-            else if (cmd->SCp.Status != GOOD)
-               cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
             hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
             hostdata->state = S_UNCONNECTED;
+            if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+               cmd->SCp.Status = lun;
+            if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+               cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+            else
+               cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
             cmd->scsi_done(cmd);
 
 /* We are no longer connected to a target - check to see if
@@ -1452,10 +1462,10 @@
          hostdata->connected = NULL;
          hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
          hostdata->state = S_UNCONNECTED;
-         if (cmd->cmnd[0] != REQUEST_SENSE)
-            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-         else if (cmd->SCp.Status != GOOD)
+         if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
             cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+         else
+            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
          cmd->scsi_done(cmd);
 
 /* We are no longer connected to a target - check to see if
@@ -1485,10 +1495,11 @@
                hostdata->connected = NULL;
                hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
                hostdata->state = S_UNCONNECTED;
-               if (cmd->cmnd[0] != REQUEST_SENSE)
-                  cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-               else if (cmd->SCp.Status != GOOD)
+DB(DB_INTR,printk(":%d",cmd->SCp.Status))
+               if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
                   cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+               else
+                  cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
                cmd->scsi_done(cmd);
                break;
             case S_PRE_TMP_DISC:
@@ -1498,8 +1509,8 @@
                hostdata->connected = NULL;
                hostdata->state = S_UNCONNECTED;
 
-#ifdef PROC_INTERFACE
-               disc_taken_total++;
+#ifdef PROC_STATISTICS
+               hostdata->disc_done_cnt[cmd->target]++;
 #endif
 
                break;
@@ -1600,7 +1611,7 @@
     * But we DO need to fix the DPD bit so it's correct for this command.
     */
 
-         if (IS_DIR_OUT(cmd))
+         if (is_dir_out(cmd))
             write_3393(hostdata,WD_DESTINATION_ID,cmd->target);
          else
             write_3393(hostdata,WD_DESTINATION_ID,cmd->target | DSTID_DPD);
@@ -1633,7 +1644,7 @@
 #define RESET_CARD_AND_BUS 1
 #define B_FLAG 0x80
 
-int reset_hardware(struct Scsi_Host *instance, int type)
+static int reset_hardware(struct Scsi_Host *instance, int type)
 {
 struct IN2000_hostdata *hostdata;
 int qt,x;
@@ -1855,10 +1866,11 @@
 
 
 #define MAX_IN2000_HOSTS 3
-#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
 #define SETUP_BUFFER_SIZE 200
 static char setup_buffer[SETUP_BUFFER_SIZE];
-static char setup_used[MAX_SETUP_STRINGS];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
 
 void in2000_setup (char *str, int *ints)
 {
@@ -1869,43 +1881,44 @@
    setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
    p1 = setup_buffer;
    i = 0;
-   while (*p1 && (i < MAX_SETUP_STRINGS)) {
+   while (*p1 && (i < MAX_SETUP_ARGS)) {
       p2 = strchr(p1, ',');
       if (p2) {
          *p2 = '\0';
          if (p1 != p2)
-            setup_strings[i] = p1;
+            setup_args[i] = p1;
          p1 = p2 + 1;
          i++;
          }
       else {
-         setup_strings[i] = p1;
+         setup_args[i] = p1;
          break;
          }
       }
-   for (i=0; i<MAX_SETUP_STRINGS; i++)
+   for (i=0; i<MAX_SETUP_ARGS; i++)
       setup_used[i] = 0;
+   done_setup = 1;
 }
 
 
-/* check_setup_strings() returns index if key found, 0 if not
+/* check_setup_args() returns index if key found, 0 if not
  */
 
-int check_setup_strings(char *key, int *flags, int *val, char *buf)
+static int check_setup_args(char *key, int *flags, int *val, char *buf)
 {
 int x;
 char *cp;
 
-   for  (x=0; x<MAX_SETUP_STRINGS; x++) {
+   for  (x=0; x<MAX_SETUP_ARGS; x++) {
       if (setup_used[x])
          continue;
-      if (!strncmp(setup_strings[x], key, strlen(key)))
+      if (!strncmp(setup_args[x], key, strlen(key)))
          break;
       }
-   if (x == MAX_SETUP_STRINGS)
+   if (x == MAX_SETUP_ARGS)
       return 0;
    setup_used[x] = 1;
-   cp = setup_strings[x] + strlen(key);
+   cp = setup_args[x] + strlen(key);
    *val = -1;
    if (*cp != ':')
       return ++x;
@@ -1918,33 +1931,33 @@
 
 
 
-struct proc_dir_entry proc_scsi_in2000 = {
-   PROC_SCSI_IN2000, 6, "in2000",
-   S_IFDIR | S_IRUGO | S_IXUGO, 2
-   };
-
-
-const unsigned int *bios_tab[] = {
+/* The "correct" (ie portable) way to access memory-mapped hardware
+ * such as the IN2000 EPROM and dip switch is through the use of
+ * special macros declared in 'asm/io.h'. We use readb() and readl()
+ * when reading from the card's BIOS area in in2000_detect().
+ */
+static const unsigned int *bios_tab[] = {
    (unsigned int *)0xc8000,
    (unsigned int *)0xd0000,
    (unsigned int *)0xd8000,
    0
    };
 
-const unsigned short base_tab[] = {
+static const unsigned short base_tab[] = {
    0x220,
    0x200,
    0x110,
    0x100,
    };
 
-const int int_tab[] = {
+static const int int_tab[] = {
    15,
    14,
    11,
    10
    };
 
+
 int in2000_detect(Scsi_Host_Template * tpnt)
 {
 struct Scsi_Host *instance;
@@ -1960,36 +1973,40 @@
 char buf[32];
 
 /* Thanks to help from Bill Earnest, probing for IN2000 cards is a
- * pretty straightforward and fool-proof operation. We do require
- * that cards have their BIOS enabled, although I hope to be able
- * to detect and use BIOS-less cards in the future. There are 3
+ * pretty straightforward and fool-proof operation. There are 3
  * possible locations for the IN2000 EPROM in memory space - if we
  * find a BIOS signature, we can read the dip switch settings from
  * the byte at BIOS+32 (shadowed in by logic on the card). From 2
  * of the switch bits we get the card's address in IO space. There's
  * an image of the dip switch there, also, so we have a way to back-
- * check that this really is an IN2000 card. Very nifty.
- *
- * There have been a couple of BIOS versions with different layouts
- * for the obvious ID strings. We look for the 2 most common ones and
- * hope that they cover all the cases...
+ * check that this really is an IN2000 card. Very nifty. Use the
+ * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
+ * or disabled.
  */
 
+   if (!done_setup && setup_strings)
+      in2000_setup(setup_strings,0);
+
    detect_count = 0;
    for (bios = 0; bios_tab[bios]; bios++) {
-      if (check_setup_strings("ioport",&flags,&val,buf)) {
+      if (check_setup_args("ioport",&flags,&val,buf)) {
          base = val;
          switches = ~inb(base + IO_SWITCHES) & 0xff;
          printk("Forcing IN2000 detection at IOport 0x%x ",base);
          bios = 2;
          }
-      else if (*(bios_tab[bios]+0x04) == 0x41564f4e ||
-          *(bios_tab[bios]+0x0c) == 0x61776c41) {
+/*
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
+      else if (readl(bios_tab[bios]+0x04) == 0x41564f4e ||
+               readl(bios_tab[bios]+0x0c) == 0x61776c41) {
          printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]);
 
 /* Read the switch image that's mapped into EPROM space */
 
-         switches = ~((*(bios_tab[bios]+0x08) & 0xff));
+         switches = ~((readb(bios_tab[bios]+0x08) & 0xff));
 
 /* Find out where the IO space is */
 
@@ -2066,6 +2083,11 @@
          hostdata->busy[x] = 0;
          hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
          hostdata->sync_stat[x] = SS_UNSET;  /* using default sync values */
+#ifdef PROC_STATISTICS
+         hostdata->cmd_cnt[x] = 0;
+         hostdata->disc_allowed_cnt[x] = 0;
+         hostdata->disc_done_cnt[x] = 0;
+#endif
          }
       hostdata->input_Q = NULL;
       hostdata->selecting = NULL;
@@ -2082,41 +2104,47 @@
 
 /* Older BIOS's had a 'sync on/off' switch - use its setting */
 
-      if (*(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
+      if (readl(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
          hostdata->sync_off = 0x00;    /* sync defaults to on */
       else
          hostdata->sync_off = 0xff;    /* sync defaults to off */
 
-      hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+#ifdef PROC_INTERFACE
+      hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS|
                        PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
                        PR_STOP;
-
-#ifdef PROC_INTERFACE
-      disc_allowed_total = 0;
-      disc_taken_total = 0;
+#ifdef PROC_STATISTICS
+      hostdata->int_cnt = 0;
+#endif
 #endif
 
-      if (check_setup_strings("nosync",&flags,&val,buf))
+      if (check_setup_args("nosync",&flags,&val,buf))
          hostdata->sync_off = val;
 
-      if (check_setup_strings("period",&flags,&val,buf))
+      if (check_setup_args("period",&flags,&val,buf))
          hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
 
-      if (check_setup_strings("disconnect",&flags,&val,buf)) {
+      if (check_setup_args("disconnect",&flags,&val,buf)) {
          if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
             hostdata->disconnect = val;
          else
             hostdata->disconnect = DIS_ADAPTIVE;
          }
 
-      if (check_setup_strings("noreset",&flags,&val,buf))
+      if (check_setup_args("noreset",&flags,&val,buf))
          hostdata->args ^= A_NO_SCSI_RESET;
 
-      if (check_setup_strings("debug",&flags,&val,buf))
+      if (check_setup_args("level2",&flags,&val,buf))
+         hostdata->level2 = val;
+
+      if (check_setup_args("debug",&flags,&val,buf))
          hostdata->args = (val & DB_MASK);
 
-      if (check_setup_strings("proc",&flags,&val,buf))
+#ifdef PROC_INTERFACE
+      if (check_setup_args("proc",&flags,&val,buf))
          hostdata->proc = val;
+#endif
+
 
       x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS);
 
@@ -2142,9 +2170,9 @@
                   (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
                   hostdata->microcode);
 #ifdef DEBUGGING_ON
-      printk("setup_strings = ");
-      for (x=0; x<8; x++)
-         printk("%s,",setup_strings[x]);
+      printk("setup_args = ");
+      for (x=0; x<MAX_SETUP_ARGS; x++)
+         printk("%s,",setup_args[x]);
       printk("\n");
 #endif
       if (hostdata->sync_off == 0xff)
@@ -2188,31 +2216,16 @@
       iinfo[0] = 255;
       iinfo[1] = 63;
       iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-      if (iinfo[2] > 1023)
-         iinfo[2] = 1023;
       }
     return 0;
 }
 
 
-#ifdef PROC_INTERFACE
-
-/* Certain older compilers (such as a.out 2.5.8) choke and give a
- * "Too many reloads" error when there are a lot of calls to 'strcat()'
- * in one function. Modern kernels define 'strcat()' as an inline
- * function - I _guess_ this is related to the problem. Regardless,
- * we can make everyone happy by doing some macro fudging to force
- * gcc to do calls instead of inline expansion.
- */
-
-char * in2000_strcat(char * dest, const char * src)
-{
-   return strcat(dest,src);
-}
-
-#define strcat(d,s) (in2000_strcat((d),(s)))
 
-#endif
+struct proc_dir_entry proc_scsi_in2000 = {
+   PROC_SCSI_IN2000, 6, "in2000",
+   S_IFDIR | S_IRUGO | S_IXUGO, 2
+   };
 
 
 int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
@@ -2278,6 +2291,10 @@
          bp += 5;
          hd->proc = simple_strtoul(bp,NULL,0);
          }
+      else if (!strncmp(bp,"level2:",7)) {
+         bp += 7;
+         hd->level2 = simple_strtoul(bp,NULL,0);
+         }
       return len;
       }
 
@@ -2296,12 +2313,38 @@
                   (hd->dip_switch & 0x40)?"Yes":"No",
                   (hd->dip_switch & 0x20)?"Yes":"No");
       strcat(bp,tbuf);
+      strcat(bp,"\nsync_xfer[] =       ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%02x",hd->sync_xfer[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\nsync_stat[] =       ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%02x",hd->sync_stat[x]);
+         strcat(bp,tbuf);
+         }
       }
-   if (hd->proc & PR_TOTALS) {
-      sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
-            disc_allowed_total,disc_taken_total);
+#ifdef PROC_STATISTICS
+   if (hd->proc & PR_STATISTICS) {
+      strcat(bp,"\ncommands issued:    ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\ndisconnects allowed:");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\ndisconnects done:   ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      sprintf(tbuf,"\ninterrupts:      \t%ld",hd->int_cnt);
       strcat(bp,tbuf);
       }
+#endif
    if (hd->proc & PR_CONNECTED) {
       strcat(bp,"\nconnected:     ");
       if (hd->connected) {
diff -urN linux-2.0.31-pre3/drivers/scsi/in2000.h linux-2.0.31-pre4/drivers/scsi/in2000.h
--- linux-2.0.31-pre3/drivers/scsi/in2000.h	1996-05-09 06:40:24.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/in2000.h	2003-08-15 15:03:56.000000000 -0700
@@ -2,7 +2,7 @@
  *    in2000.h -  Linux device driver definitions for the
  *                Always IN2000 ISA SCSI card.
  *
- *    IMPORTANT: This file is for version 1.28 - 07/May/1996
+ *    IMPORTANT: This file is for version 1.31 - 06/Jul/1997
  *
  * Copyright (c) 1996 John Shifflett, GeoLog Consulting
  *    john@geolog.com
@@ -25,9 +25,66 @@
 
 #include <asm/io.h>
 
+#define PROC_INTERFACE     /* add code for /proc/scsi/in2000/xxx interface */
+#ifdef  PROC_INTERFACE
+#define PROC_STATISTICS    /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG         /* extra info on sync negotiation printed */
+#define DEBUGGING_ON       /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0   /* default bitmask - change from command-line */
+
+#define FAST_READ_IO       /* No problems with these on my machine */
+#define FAST_WRITE_IO
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
+#else
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
 
 #define uchar unsigned char
 
+#define read1_io(a)     (inb(hostdata->io_base+(a)))
+#define read2_io(a)     (inw(hostdata->io_base+(a)))
+#define write1_io(b,a)  (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a)  (outw((w),hostdata->io_base+(a)))
+
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO()    \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   insw %%dx              \n \
+1: "                       \
+   : "=D" (sp)                   /* output */   \
+   : "d" (f), "D" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "edi" )       /* trashed */
+
+#define FAST_WRITE2_IO()   \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   outsw %%dx             \n \
+1: "                       \
+   : "=S" (sp)                   /* output */   \
+   : "d" (f), "S" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "esi" )       /* trashed */
+
 
 /* IN2000 io_port offsets */
 #define IO_WD_ASR       0x00     /* R - 3393 auxstat reg */
@@ -194,6 +251,9 @@
 
 
 
+#define ILLEGAL_STATUS_BYTE   0xff
+
+
 #define DEFAULT_SX_PER     500   /* (ns) fairly safe */
 #define DEFAULT_SX_OFF     0     /* aka async */
 
@@ -231,7 +291,15 @@
     uchar            sync_xfer[8];     /* sync_xfer reg settings per target */
     uchar            sync_stat[8];     /* status of sync negotiation per target */
     uchar            sync_off;         /* bit mask: don't use sync with these targets */
+#ifdef PROC_INTERFACE
     uchar            proc;             /* bit mask: what's in proc output */
+#ifdef PROC_STATISTICS
+    unsigned long    cmd_cnt[8];       /* # of commands issued per target */
+    unsigned long    int_cnt;          /* # of interrupts serviced */
+    unsigned long    disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+    unsigned long    disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
     };
 
 
@@ -301,7 +369,7 @@
 
 #define PR_VERSION   1<<0
 #define PR_INFO      1<<1
-#define PR_TOTALS    1<<2
+#define PR_STATISTICS 1<<2
 #define PR_CONNECTED 1<<3
 #define PR_INPUTQ    1<<4
 #define PR_DISCQ     1<<5
diff -urN linux-2.0.31-pre3/drivers/scsi/in2000.readme linux-2.0.31-pre4/drivers/scsi/in2000.readme
--- linux-2.0.31-pre3/drivers/scsi/in2000.readme	1996-06-06 03:00:20.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/in2000.readme	1969-12-31 16:00:00.000000000 -0800
@@ -1,155 +0,0 @@
-
-UPDATE NEWS: version 1.28 - 07 May 96
-
-   Tightened up the "interrupts enabled/disabled" discipline
-   in 'in2000_queuecommand()' and maybe 1 or 2 other places.
-   I _think_ it may have been a little too lax, causing an
-   occasional crash during full moon. A fully functional
-   /proc interface is now in place - if you want to play
-   with it, start by doing 'cat /proc/scsi/in2000/0'. You
-   can also use it to change a few run-time parameters on
-   the fly, but it's mostly for debugging. The curious
-   should take a good look at 'in2000_proc_info()' in the
-   in2000.c file to get an understanding of what it's all
-   about; I figure that people who are really into it will
-   want to add features suited to their own needs...
-   Also, sync is now DISABLED by default.
-
-UPDATE NEWS: version 1.27 - 10 Apr 96
-
-   Fixed a well-hidden bug in the adaptive-disconnect code
-   that would show up every now and then during extreme
-   heavy loads involving 2 or more simultaneously active
-   devices. Thanks to Joe Mack for keeping my nose to the
-   grindstone on this one.
-
-UPDATE NEWS: version 1.26 - 07 Mar 96
-
-   1.25 had a nasty bug that bit people with swap partitions
-   and tape drives. Also, in my attempt to guess my way
-   through Intel assembly language, I made an error in the
-   inline code for IO writes. Made a few other changes and
-   repairs - this version (fingers crossed) should work well.
-
-UPDATE NEWS: version 1.25 - 05 Mar 96
-
-   Kernel 1.3.70 interrupt mods added; old kernels still OK.
-   Big help from Bill Earnest and David Willmore on speed
-   testing and optimizing: I think there's a real improvement
-   in this area.
-   New! User-friendly command-line interface for LILO and
-   module loading - the old method is gone, so you'll need
-   to read the comments for 'setup_strings' near the top
-   of in2000.c. For people with CDROM's or other devices
-   that have a tough time with sync negotiation, you can
-   now selectively disable sync on individual devices -
-   search for the 'nosync' keyword in the command-line
-   comments. Some of you disable the BIOS on the card, which
-   caused the auto-detect function to fail; there is now a
-   command-line option to force detection of a ROM-less card.
-
-UPDATE NEWS: version 1.24a - 24 Feb 96
-
-   There was a bug in the synchronous transfer code. Only
-   a few people downloaded before I caught it - could have
-   been worse.
-
-UPDATE NEWS: version 1.24 - 23 Feb 96
-
-   Lots of good changes. Advice from Bill Earnest resulted
-   in much better detection of cards, more efficient usage
-   of the fifo, and (hopefully) faster data transfers. The
-   jury is still out on speed - I hope it's improved some.
-   One nifty new feature is a cool way of doing disconnect/
-   reselect. The driver defaults to what I'm calling
-   'adaptive disconnect' - meaning that each command is
-   evaluated individually as to whether or not it should be
-   run with the option to disconnect/reselect (if the device
-   chooses), or as a "SCSI-bus-hog". When several devices
-   are operating simultaneously, disconnects are usually an
-   advantage. In a single device system, or if only 1 device
-   is being accessed, transfers usually go faster if disconnects
-   are not allowed.
-
-
-
-The default arguments (you get these when you don't give an 'in2000'
-command-line argument, or you give a blank argument) will cause
-the driver to do adaptive disconnect, synchronous transfers, and a
-minimum of debug messages. If you want to fool with the options,
-search for 'setup_strings' near the top of the in2000.c file and
-check the 'hostdata->args' section in in2000.h - but be warned! Not
-everything is working yet (some things will never work, probably).
-I believe that disabling disconnects (DIS_NEVER) will allow you
-to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't
-spent a lot of time testing this. You might try 'ENABLE_CLUSTERING'
-to see what happens: my tests showed little difference either way.
-There's also a define called 'DEFAULT_SX_PER'; this sets the data
-transfer speed for the asynchronous mode. I've put it at 500 ns
-despite the fact that the card could handle settings of 376 or
-252, because I'm not really sure if certain devices or maybe bad
-cables might have trouble at higher speeds. I couldn't find any
-info in my various SCSI references that talk about this in language
-I could understand, so decided to compromise with 500. This is still
-faster than the old driver was set at (I think). Can someone explain
-the significance of the bus transfer speed setting? Do devices on
-the bus ever care what it is? Is cable quality a factor here?
-Regardless, you can choose your own default through the command-
-line with the 'period' keyword.
-
-
-------------------------------------------------
-***********  DIP switch settings  **************
-------------------------------------------------
-
-   sw1-1 sw1-2    BIOS address (hex)
-   -----------------------------------------
-    off   off     C8000 - CBFF0
-    on    off     D8000 - DBFF0
-    off   on      D0000 - D3FF0
-    on    on      BIOS disabled
-
-   sw1-3 sw1-4    IO port address (hex)
-   ------------------------------------
-    off   off     220 - 22F
-    on    off     200 - 20F
-    off   on      110 - 11F
-    on    on      100 - 10F
-
-   sw1-5 sw1-6 sw1-7    Interrupt
-   ------------------------------
-    off   off   off     15
-    off   on    off     14
-    off   off   on      11
-    off   on    on      10
-    on    -     -       disabled
-
-   sw1-8 function depends on BIOS version. In earlier versions this
-   controlled synchronous data transfer support for MSDOS:
-      off = disabled
-      on  = enabled
-   In later ROMs (starting with 01.3 in April 1994) sw1-8 controls
-   the "greater than 2 disk drive" feature that first appeared in
-   MSDOS 5.0 (ignored by linux):
-      off = 2 drives maximum
-      on  = 7 drives maximum
-
-   sw1-9    Floppy controller
-   --------------------------
-    off     disabled
-    on      enabled
-
-------------------------------------------------
-
-   I should mention that Drew Eckhardt's 'Generic NCR5380' sources
-   were my main inspiration, with lots of reference to the IN2000
-   driver currently distributed in the kernel source. I also owe
-   much to a driver written by Hamish Macdonald for Linux-m68k(!).
-   And to Eric Wright for being an ALPHA guinea pig. And to Bill
-   Earnest for 2 tons of great input and information. And to David
-   Willmore for extensive 'bonnie' testing. And to Joe Mack for
-   continual testing and feedback.
-
-
-            John Shifflett    jshiffle@netcom.com
-
diff -urN linux-2.0.31-pre3/drivers/scsi/Makefile linux-2.0.31-pre4/drivers/scsi/Makefile
--- linux-2.0.31-pre3/drivers/scsi/Makefile	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/Makefile	2003-08-15 15:03:56.000000000 -0700
@@ -260,10 +260,8 @@
 
 ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),y)
 L_OBJS += g_NCR5380.o
-EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
 else
   ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),m)
-  EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
   M_OBJS += g_NCR5380.o
   endif
 endif
@@ -382,10 +380,6 @@
 seagate.o: seagate.c
 	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
 
-# For debugging, use the -g flag
-53c7,8xx.o : 53c7,8xx.c
-	$(CC) $(CFLAGS) -g -c 53c7,8xx.c
-
 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake.c
 	$(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl 
@@ -393,8 +387,8 @@
 	mv scriptu.h 53c8xx_u.h
 	rm fake.c
 
-ncr53c8xx.o : ncr53c8xx.c
-	$(CC) $(CFLAGS) -c ncr53c8xx.c
+g_NCR5380.o: g_NCR5380.c
+	$(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c
 
 scsi_mod.o: $(MX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o
diff -urN linux-2.0.31-pre3/drivers/scsi/README.BusLogic linux-2.0.31-pre4/drivers/scsi/README.BusLogic
--- linux-2.0.31-pre3/drivers/scsi/README.BusLogic	1997-03-29 09:01:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/README.BusLogic	2003-08-15 15:03:56.000000000 -0700
@@ -1,10 +1,10 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			  Version 2.0.9 for Linux 2.0
+			 Version 2.0.10 for Linux 2.0
 
 			      PRODUCTION RELEASE
 
-				 29 March 1997
+				11 August 1997
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -284,6 +284,14 @@
   so as to recognize the host adapters in the same order as they are enumerated
   by the host adapter's BIOS.
 
+o Mega-Transfers/Second
+
+  The driver reports on the synchronous transfer parameters negotiated between
+  the host adapter and target devices in units of "mega-transfers/second".  For
+  wide devices, the unit of transfer is 16 bits if wide negotiation has been
+  successfully completed.  Therefore, the total transfer rate to wide devices
+  will generally be twice the synchronous tranfer rate reported by the driver.
+
 
 			     COMMAND LINE OPTIONS
 
@@ -372,16 +380,17 @@
 
 				 INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.0.29, but should be
-compatible with 2.0.4 or any later 2.0 series kernel.
+This distribution was prepared for Linux kernel version 2.0.30, but should be
+compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also
+applied.
 
 To install the new BusLogic SCSI driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.9.tar.gz
+  tar -xvzf BusLogic-2.0.10.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
-  patch -p < BusLogic.patch
+  patch -p < BusLogic.patch	    # Only for kernels prior to 2.0.30
   cd linux
   make config
   make depend
diff -urN linux-2.0.31-pre3/drivers/scsi/README.in2000 linux-2.0.31-pre4/drivers/scsi/README.in2000
--- linux-2.0.31-pre3/drivers/scsi/README.in2000	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/README.in2000	2003-08-15 15:03:56.000000000 -0700
@@ -0,0 +1,180 @@
+
+UPDATE NEWS: version 1.31 - 6 Jul 97
+
+   Fixed a bug that caused incorrect SCSI status bytes to be
+   returned from commands sent to LUN's greater than 0. This
+   means that CDROM changers work now! Fixed a bug in the
+   handling of command-line arguments when loaded as a module.
+   Also put all the header data in in2000.h where it belongs.
+   There are no longer any differences between this driver in
+   the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31
+   and 2.1.45 (or is it .46?) - this makes things much easier
+   for me...
+
+UPDATE NEWS: version 1.30 - 14 Oct 96
+
+   Fixed a bug in the code that sets the transfer direction
+   bit (DESTID_DPD in the WD_DESTINATION_ID register). There
+   are quite a few SCSI commands that do a write-to-device;
+   now we deal with all of them correctly. Thanks to Joerg
+   Dorchain for catching this one.
+
+UPDATE NEWS: version 1.29 - 24 Sep 96
+
+   The memory-mapped hardware on the card is now accessed via
+   the 'readb()' and 'readl()' macros - required by the new
+   memory management scheme in the 2.1.x kernel series.
+   As suggested by Andries Brouwer, 'bios_param()' no longer
+   forces an artificial 1023 track limit on drives. Also
+   removed some kludge-code left over from struggles with
+   older (buggy) compilers.
+
+UPDATE NEWS: version 1.28 - 07 May 96
+
+   Tightened up the "interrupts enabled/disabled" discipline
+   in 'in2000_queuecommand()' and maybe 1 or 2 other places.
+   I _think_ it may have been a little too lax, causing an
+   occasional crash during full moon. A fully functional
+   /proc interface is now in place - if you want to play
+   with it, start by doing 'cat /proc/scsi/in2000/0'. You
+   can also use it to change a few run-time parameters on
+   the fly, but it's mostly for debugging. The curious
+   should take a good look at 'in2000_proc_info()' in the
+   in2000.c file to get an understanding of what it's all
+   about; I figure that people who are really into it will
+   want to add features suited to their own needs...
+   Also, sync is now DISABLED by default.
+
+UPDATE NEWS: version 1.27 - 10 Apr 96
+
+   Fixed a well-hidden bug in the adaptive-disconnect code
+   that would show up every now and then during extreme
+   heavy loads involving 2 or more simultaneously active
+   devices. Thanks to Joe Mack for keeping my nose to the
+   grindstone on this one.
+
+UPDATE NEWS: version 1.26 - 07 Mar 96
+
+   1.25 had a nasty bug that bit people with swap partitions
+   and tape drives. Also, in my attempt to guess my way
+   through Intel assembly language, I made an error in the
+   inline code for IO writes. Made a few other changes and
+   repairs - this version (fingers crossed) should work well.
+
+UPDATE NEWS: version 1.25 - 05 Mar 96
+
+   Kernel 1.3.70 interrupt mods added; old kernels still OK.
+   Big help from Bill Earnest and David Willmore on speed
+   testing and optimizing: I think there's a real improvement
+   in this area.
+   New! User-friendly command-line interface for LILO and
+   module loading - the old method is gone, so you'll need
+   to read the comments for 'setup_strings' near the top
+   of in2000.c. For people with CDROM's or other devices
+   that have a tough time with sync negotiation, you can
+   now selectively disable sync on individual devices -
+   search for the 'nosync' keyword in the command-line
+   comments. Some of you disable the BIOS on the card, which
+   caused the auto-detect function to fail; there is now a
+   command-line option to force detection of a ROM-less card.
+
+UPDATE NEWS: version 1.24a - 24 Feb 96
+
+   There was a bug in the synchronous transfer code. Only
+   a few people downloaded before I caught it - could have
+   been worse.
+
+UPDATE NEWS: version 1.24 - 23 Feb 96
+
+   Lots of good changes. Advice from Bill Earnest resulted
+   in much better detection of cards, more efficient usage
+   of the fifo, and (hopefully) faster data transfers. The
+   jury is still out on speed - I hope it's improved some.
+   One nifty new feature is a cool way of doing disconnect/
+   reselect. The driver defaults to what I'm calling
+   'adaptive disconnect' - meaning that each command is
+   evaluated individually as to whether or not it should be
+   run with the option to disconnect/reselect (if the device
+   chooses), or as a "SCSI-bus-hog". When several devices
+   are operating simultaneously, disconnects are usually an
+   advantage. In a single device system, or if only 1 device
+   is being accessed, transfers usually go faster if disconnects
+   are not allowed.
+
+
+
+The default arguments (you get these when you don't give an 'in2000'
+command-line argument, or you give a blank argument) will cause
+the driver to do adaptive disconnect, synchronous transfers, and a
+minimum of debug messages. If you want to fool with the options,
+search for 'setup_strings' near the top of the in2000.c file and
+check the 'hostdata->args' section in in2000.h - but be warned! Not
+everything is working yet (some things will never work, probably).
+I believe that disabling disconnects (DIS_NEVER) will allow you
+to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't
+spent a lot of time testing this. You might try 'ENABLE_CLUSTERING'
+to see what happens: my tests showed little difference either way.
+There's also a define called 'DEFAULT_SX_PER'; this sets the data
+transfer speed for the asynchronous mode. I've put it at 500 ns
+despite the fact that the card could handle settings of 376 or
+252, because higher speeds may be a problem with poor quality
+cables or improper termination; 500 ns is a compromise. You can
+choose your own default through the command-line with the
+'period' keyword.
+
+
+------------------------------------------------
+***********  DIP switch settings  **************
+------------------------------------------------
+
+   sw1-1 sw1-2    BIOS address (hex)
+   -----------------------------------------
+    off   off     C8000 - CBFF0
+    on    off     D8000 - DBFF0
+    off   on      D0000 - D3FF0
+    on    on      BIOS disabled
+
+   sw1-3 sw1-4    IO port address (hex)
+   ------------------------------------
+    off   off     220 - 22F
+    on    off     200 - 20F
+    off   on      110 - 11F
+    on    on      100 - 10F
+
+   sw1-5 sw1-6 sw1-7    Interrupt
+   ------------------------------
+    off   off   off     15
+    off   on    off     14
+    off   off   on      11
+    off   on    on      10
+    on    -     -       disabled
+
+   sw1-8 function depends on BIOS version. In earlier versions this
+   controlled synchronous data transfer support for MSDOS:
+      off = disabled
+      on  = enabled
+   In later ROMs (starting with 01.3 in April 1994) sw1-8 controls
+   the "greater than 2 disk drive" feature that first appeared in
+   MSDOS 5.0 (ignored by Linux):
+      off = 2 drives maximum
+      on  = 7 drives maximum
+
+   sw1-9    Floppy controller
+   --------------------------
+    off     disabled
+    on      enabled
+
+------------------------------------------------
+
+   I should mention that Drew Eckhardt's 'Generic NCR5380' sources
+   were my main inspiration, with lots of reference to the IN2000
+   driver currently distributed in the kernel source. I also owe
+   much to a driver written by Hamish Macdonald for Linux-m68k(!).
+   And to Eric Wright for being an ALPHA guinea pig. And to Bill
+   Earnest for 2 tons of great input and information. And to David
+   Willmore for extensive 'bonnie' testing. And to Joe Mack for
+   continual testing and feedback.
+
+
+            John Shifflett    jshiffle@netcom.com
+
diff -urN linux-2.0.31-pre3/drivers/scsi/scsi.c linux-2.0.31-pre4/drivers/scsi/scsi.c
--- linux-2.0.31-pre3/drivers/scsi/scsi.c	1997-03-31 13:27:43.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/scsi.c	2003-08-15 15:03:56.000000000 -0700
@@ -114,7 +114,6 @@
 #define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
 			  || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
 
-#define MAX_SCSI_DEVICE_CODE 10
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
 {
     "Direct-Access    ",
@@ -289,6 +288,7 @@
 {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"YAMAHA","CDR100","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
+{"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
 /*
  * Must be at end of list...
  */
diff -urN linux-2.0.31-pre3/drivers/scsi/scsi.h linux-2.0.31-pre4/drivers/scsi/scsi.h
--- linux-2.0.31-pre3/drivers/scsi/scsi.h	1997-03-11 15:40:02.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/scsi.h	2003-08-15 15:03:56.000000000 -0700
@@ -39,11 +39,12 @@
 extern volatile int in_scan_scsis;
 extern const unsigned char scsi_command_size[8];
 #define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
-
 #define IDENTIFY_BASE       0x80
 #define IDENTIFY(can_disconnect, lun)   (IDENTIFY_BASE |\
 		     ((can_disconnect) ?  0x40 : 0) |\
 		     ((lun) & 0x07)) 
+#define MAX_SCSI_DEVICE_CODE 10
+extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 
 		 
     
@@ -218,6 +219,12 @@
 
 extern Scsi_Device * scsi_devices;
 
+extern struct hd_struct * sd;
+
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+extern struct hd_struct * sd;
+#endif
+
 /*
  *  Initializes all SCSI devices.  This scans all scsi busses.
  */ 
@@ -498,8 +505,14 @@
     req = &SCpnt->request;
     req->errors = 0;
     if (!uptodate) {
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+	printk(DEVICE_NAME " I/O error: dev %s, sector %lu, absolute sector %lu\n",
+	       kdevname(req->rq_dev), req->sector, 
+	       req->sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+#else
 	printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
 	       kdevname(req->rq_dev), req->sector);
+#endif
     }
     
     do {
diff -urN linux-2.0.31-pre3/drivers/scsi/scsi_ioctl.c linux-2.0.31-pre4/drivers/scsi/scsi_ioctl.c
--- linux-2.0.31-pre3/drivers/scsi/scsi_ioctl.c	1997-02-28 15:14:18.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/scsi_ioctl.c	2003-08-15 15:03:56.000000000 -0700
@@ -21,8 +21,13 @@
 #include "hosts.h"
 #include <scsi/scsi_ioctl.h>
 
-#define MAX_RETRIES 5   
-#define MAX_TIMEOUT (9 * HZ)
+#define NORMAL_RETRIES 5   
+#define NORMAL_TIMEOUT (10 * HZ)
+#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
+#define START_STOP_TIMEOUT (60 * HZ)
+#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
+#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
+
 #define MAX_BUF 4096
 
 #define max(a,b) (((a) > (b)) ? (a) : (b))
@@ -65,7 +70,7 @@
 /*
  * 
  * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
- * The MAX_TIMEOUT and MAX_RETRIES  variables are used.  
+ * The NORMAL_TIMEOUT and NORMAL_RETRIES  variables are used.  
  * 
  * dev is the SCSI device struct ptr, *(int *) arg is the length of the
  * input data, if any, not including the command string & counts, 
@@ -101,7 +106,8 @@
     }
 }   
 
-static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
+static int ioctl_internal_command(Scsi_Device *dev, char * cmd,
+				  int timeout, int retries)
 {
     int result;
     Scsi_Cmnd * SCpnt;
@@ -110,9 +116,7 @@
     {
 	struct semaphore sem = MUTEX_LOCKED;
 	SCpnt->request.sem = &sem;
-	scsi_do_cmd(SCpnt,  cmd, NULL,  0,
-		    scsi_ioctl_done,  MAX_TIMEOUT,
-		    MAX_RETRIES);
+	scsi_do_cmd(SCpnt,  cmd, NULL,  0, scsi_ioctl_done,  timeout, retries);
 	down(&sem);
     }
     
@@ -243,21 +247,24 @@
     switch (opcode)
       {
       case FORMAT_UNIT:
-	timeout =  2 * 60 * 60 * HZ; /* 2 Hours */
+	timeout = FORMAT_UNIT_TIMEOUT;
 	retries = 1;
 	break;
       case START_STOP:
-	timeout =  60 * HZ;	/* 60 seconds */
-	retries = 1;
+	timeout = START_STOP_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       case MOVE_MEDIUM:
+	timeout = MOVE_MEDIUM_TIMEOUT;
+	retries = NORMAL_RETRIES;
+	break;
       case READ_ELEMENT_STATUS:
-	timeout =  5 * 60 * HZ;	/* 5 minutes */
-	retries = 1;
+	timeout = READ_ELEMENT_STATUS_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       default:
-	timeout = MAX_TIMEOUT;
-	retries = MAX_RETRIES;
+	timeout = NORMAL_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       }
 
@@ -366,7 +373,8 @@
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
 	break;
     case SCSI_IOCTL_DOORUNLOCK:
 	if (!dev->removable || !dev->lockable) return 0;
@@ -374,13 +382,31 @@
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
     case SCSI_IOCTL_TEST_UNIT_READY:
 	scsi_cmd[0] = TEST_UNIT_READY;
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = 0;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
+	break;
+    case SCSI_IOCTL_START_UNIT:
+	scsi_cmd[0] = START_STOP;
+	scsi_cmd[1] = dev->lun << 5;
+	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+	scsi_cmd[4] = 1;
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      START_STOP_TIMEOUT, NORMAL_RETRIES);
+	break;
+    case SCSI_IOCTL_STOP_UNIT:
+	scsi_cmd[0] = START_STOP;
+	scsi_cmd[1] = dev->lun << 5;
+	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+	scsi_cmd[4] = 0;
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      START_STOP_TIMEOUT, NORMAL_RETRIES);
 	break;
     default :           
 	return -EINVAL;
diff -urN linux-2.0.31-pre3/drivers/scsi/scsi_proc.c linux-2.0.31-pre4/drivers/scsi/scsi_proc.c
--- linux-2.0.31-pre3/drivers/scsi/scsi_proc.c	1995-11-11 03:11:19.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/scsi_proc.c	2003-08-15 15:03:56.000000000 -0700
@@ -239,21 +239,6 @@
     return(cmdIndex);
 }
 
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
 {	    
     int x, y = *size;
@@ -285,7 +270,7 @@
     
     y += sprintf(buffer + len + y, "  Type:   %s ",
 		     scd->type < MAX_SCSI_DEVICE_CODE ? 
-		     scsi_dev_types[(int)scd->type] : "Unknown          " );
+		     scsi_device_types[(int)scd->type] : "Unknown          " );
     y += sprintf(buffer + len + y, "               ANSI"
 		     " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
     if (scd->scsi_level == 2)
diff -urN linux-2.0.31-pre3/drivers/scsi/sd.c linux-2.0.31-pre4/drivers/scsi/sd.c
--- linux-2.0.31-pre3/drivers/scsi/sd.c	1997-02-28 15:14:18.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/sd.c	2003-08-15 15:03:56.000000000 -0700
@@ -966,7 +966,14 @@
     if(!rscsi_disks[target].device->removable) return 0;
     
     inode.i_rdev = full_dev;  /* This is all we really need here */
-    retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+    /* Using Start/Stop enables differentiation between drive with
+     * no cartridge loaded - NOT READY, drive with changed cartridge -
+     * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+     * This also handles drives that auto spin down. eg iomega jaz 1GB
+     * as this will spin up the drive.
+     */
+    retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0);
     
     if(retval){ /* Unable to test, unit probably not ready.  This usually
 		 * means there is no disc in the drive.  Mark as changed,
@@ -1023,7 +1030,7 @@
     spintime = 0;
     
     /* Spin up drives, as required.  Only do this at boot time */
-    if (!MODULE_FLAG){
+    /* Spinup needs to be done for module loads too. */
 	do{
 	    retries = 0;
 	    while(retries < 3)
@@ -1096,7 +1103,6 @@
 	    else
 		printk( "ready\n" );
 	}
-    }  /* !MODULE_FLAG */
     
     retries = 3;
     do {
diff -urN linux-2.0.31-pre3/drivers/scsi/sr.c linux-2.0.31-pre4/drivers/scsi/sr.c
--- linux-2.0.31-pre3/drivers/scsi/sr.c	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/sr.c	2003-08-15 15:03:56.000000000 -0700
@@ -545,6 +545,7 @@
 
     case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
     case SCSI_MAN_PIONEER:
+    case SCSI_MAN_UNKNOWN:
 #ifdef DEBUG
 	printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");
 #endif
@@ -563,7 +564,7 @@
                 printk(KERN_WARNING "sr_photocd: ioctl error (SONY/PIONEER): 0x%x\n",rc);
 	    break;
 	}
-	if ((rec[0] << 8) + rec[1] != 0x0a) {
+	if ((rec[0] << 8) + rec[1] < 0x0a) {
 	    printk(KERN_INFO "sr_photocd: (SONY/PIONEER) Hmm, seems the CDROM doesn't support multisession CD's\n");
 	    no_multi = 1;
 	    break;
@@ -577,7 +578,6 @@
 	break;
 		
     case SCSI_MAN_NEC_OLDCDR:
-    case SCSI_MAN_UNKNOWN:
     default:
 	sector = 0;
 	no_multi = 1;
@@ -601,6 +601,7 @@
     if (filp->f_mode & 2)  
 	return -EROFS;
 
+    sr_ioctl(inode,filp,CDROMCLOSETRAY,0);
     check_disk_change(inode->i_rdev);
     
     if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
diff -urN linux-2.0.31-pre3/drivers/scsi/st.c linux-2.0.31-pre4/drivers/scsi/st.c
--- linux-2.0.31-pre3/drivers/scsi/st.c	1997-02-28 15:14:18.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/st.c	2003-08-15 15:03:56.000000000 -0700
@@ -1120,7 +1120,8 @@
     }
 
     if (STm->do_async_writes &&
-	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
+	(((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+	  (STp->buffer)->buffer_bytes >= STp->block_size) ||
 	 STp->block_size == 0) ) {
       /* Schedule an asynchronous write */
       if (!SCpnt) {
diff -urN linux-2.0.31-pre3/drivers/scsi/u14-34f.c linux-2.0.31-pre4/drivers/scsi/u14-34f.c
--- linux-2.0.31-pre3/drivers/scsi/u14-34f.c	1997-02-26 10:56:51.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/u14-34f.c	2003-08-15 15:03:56.000000000 -0700
@@ -1,6 +1,11 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ *          Use of serial_number_at_timeout in abort and reset processing.
+ *          Use of the __initfunc and __initdata macro in setup code.
+ *          Minor cleanups in the list_statistics code.
+ *
  *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
  *          When loading as a module, parameter passing is now supported
  *          both in 2.0 and in 2.1 style.
@@ -220,10 +225,12 @@
  *  between increasing or decreasing by minimizing the seek distance between
  *  the sector of the commands just completed and the sector of the first 
  *  command in the list to be sorted. 
- *  Trivial math assures that if there are (Q-1) outstanding request for
- *  random seeks over S sectors, the unsorted average seek distance is S/2,
- *  while the sorted average seek distance is S/(Q-1). The seek distance is
- *  hence divided by a factor (Q-1)/2.
+ *  Trivial math assures that the unsorted average seek distance when doing
+ *  random seeks over S sectors is S/3.
+ *  When (Q-1) requests are uniformly distributed over S sectors, the average
+ *  distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ *  average seek distance for (Q-1) random requests over S sectors is S/Q.
+ *  The elevator sorting hence divides the seek distance by a factor Q/3.
  *  The above pure geometric remarks are valid in all cases and the 
  *  driver effectively reduces the seek distance by the predicted factor
  *  when there are Q concurrent read i/o operations on the device, but this
@@ -273,8 +280,16 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 #include "u14-34f.h"
-#include<linux/stat.h>
-#include<linux/config.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
 
 struct proc_dir_entry proc_scsi_u14_34f = {
     PROC_SCSI_U14_34F, 6, "u14_34f",
@@ -419,7 +434,7 @@
 static const char *driver_name = "Ux4F";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = { 
+static unsigned int io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -534,8 +549,7 @@
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase) {
-   unsigned int loop = MAXLOOP;
+static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
 
    while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED)
       if (--loop == 0) return TRUE;
@@ -556,7 +570,7 @@
    cpp->scsi_cdbs_len = 6;
    cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: board_inquiry, adapter busy.\n", BN(j));
       return TRUE;
       }
@@ -586,8 +600,8 @@
    return FALSE;
 }
 
-static inline int port_detect(unsigned int port_base, unsigned int j, 
-                              Scsi_Host_Template *tpnt) {
+__initfunc (static inline int port_detect \
+      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
    char *bus_type, dma_name[16];
@@ -748,7 +762,7 @@
 	 }
       }
 
-   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
    else                       sprintf(dma_name, "DMA %u", dma_channel);
 
    for (i = 0; i < sh[j]->can_queue; i++)
@@ -781,7 +795,7 @@
    return TRUE;
 }
 
-void u14_34f_setup(char *str, int *ints) {
+__initfunc (void u14_34f_setup(char *str, int *ints)) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -813,7 +827,7 @@
    return;
 }
 
-int u14_34f_detect(Scsi_Host_Template *tpnt) {
+__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
    unsigned long flags;
    unsigned int j = 0, k;
 
@@ -983,12 +997,12 @@
    if (linked_comm && SCpnt->device->queue_depth > 2
                                      && TLDEV(SCpnt->device->type)) {
       HD(j)->cp_stat[i] = READY;
-      flush_dev(SCpnt->device, 0, j, FALSE);
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
       restore_flags(flags);
       return 0;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       SCpnt->result = DID_ERROR << 16;
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy, DID_ERROR,"\
@@ -1018,7 +1032,8 @@
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
 
-   if (SCarg->host_scribble == NULL) {
+   if (SCarg->host_scribble == NULL
+       || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
 	     BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
@@ -1032,7 +1047,7 @@
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: abort, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_ABORT_ERROR;
@@ -1101,13 +1116,19 @@
    if (SCarg->host_scribble == NULL)
       printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
 
+   if (SCarg->serial_number != SCarg->serial_number_at_timeout) {
+      printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+      restore_flags(flags);
+      return SCSI_RESET_NOT_RUNNING;
+      }
+
    if (HD(j)->in_reset) {
       printk("%s: reset, exit, already in reset.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, exit, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1158,7 +1179,7 @@
       if (SCpnt == SCarg) arg_done = TRUE;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, cannot reset, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1271,7 +1292,7 @@
    unsigned int rev = FALSE, s = TRUE, r = TRUE;
    unsigned int input_only = TRUE, overlap = FALSE;
    unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
-   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
 
    static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
    static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
@@ -1282,8 +1303,8 @@
       printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
              " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
              ovlcount, readycount, readysorted, sortcount, revcount,
-             seeknosort / (readycount - batchcount + 1), 
-             seeksorted / (readycount - batchcount + 1));
+             seeknosort / (readycount + 1), 
+             seeksorted / (readycount + 1));
 
    if (n_ready <= 1) return;
 
@@ -1311,6 +1332,10 @@
 
       }
 
+   if (link_statistics) {
+      if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+      }
+
    if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
 
    if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
@@ -1328,10 +1353,11 @@
    if (overlap) sort(pl, il, n_ready, FALSE);
 
    if (link_statistics) {
+      if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
       batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
       if (input_only) inputcount++;
       if (overlap) { ovlcount++; seeksorted += seek / 1024; }
-      else seeksorted += (maxsec - minsec) / 1024;
+      else seeksorted += (iseek + maxsec - minsec) / 1024;
       if (rev && !r)     {  revcount++; readysorted += n_ready; }
       if (!rev && !s)    { sortcount++; readysorted += n_ready; }
       }
@@ -1375,7 +1401,7 @@
    for (n = 0; n < n_ready; n++) {
       k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
-      if (wait_on_busy(sh[j]->io_port)) {
+      if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
          printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
                 " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
                 SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
diff -urN linux-2.0.31-pre3/drivers/scsi/u14-34f.h linux-2.0.31-pre4/drivers/scsi/u14-34f.h
--- linux-2.0.31-pre3/drivers/scsi/u14-34f.h	1997-02-26 10:56:51.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/u14-34f.h	2003-08-15 15:03:56.000000000 -0700
@@ -11,7 +11,7 @@
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "3.00.09"
+#define U14_34F_VERSION "3.10.00"
 
 #define ULTRASTOR_14_34F {                                            \
 		NULL, /* Ptr for modules */                           \
diff -urN linux-2.0.31-pre3/drivers/scsi/wd7000.c linux-2.0.31-pre4/drivers/scsi/wd7000.c
--- linux-2.0.31-pre3/drivers/scsi/wd7000.c	1996-09-20 07:00:34.000000000 -0700
+++ linux-2.0.31-pre4/drivers/scsi/wd7000.c	2003-08-15 15:03:56.000000000 -0700
@@ -127,6 +127,10 @@
  *
  *  Thanks to Roger Scott for driver debugging.
  *
+ *  06/07/1997
+ *
+ *  Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ *  Now, driver can handle hard disks with capacity >1GB.
  */
 
 #ifdef MODULE
@@ -149,17 +153,23 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
 #define ANY2SCSI_INLINE		/* undef this to use old macros */
-#undef DEBUG
+#undef BIOSPARAM_DEBUG
+#undef DEBUG                    /* general debug */
 
 #include "wd7000.h"
-
 #include<linux/stat.h>
 
-struct proc_dir_entry proc_scsi_wd7000 = {
-    PROC_SCSI_7000FASST, 6, "wd7000",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+
+struct proc_dir_entry proc_scsi_wd7000 =
+{
+    PROC_SCSI_7000FASST,
+    6,
+    "wd7000",
+    S_IFDIR | S_IRUGO | S_IXUGO,
+    2
 };
 
 
@@ -210,12 +220,19 @@
 } Adapter;
 
 /*
+ * possible irq range
+ */
+#define IRQ_MIN   3
+#define IRQ_MAX   15
+#define IRQS      (IRQ_MAX-IRQ_MIN + 1)
+
+/*
  * The following is set up by wd7000_detect, and used thereafter by
  * wd7000_intr_handle to map the irq level to the corresponding Adapter.
  * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
  * changed to pick up the IRQ level correctly.
  */
-Adapter *irq2host[16] = {NULL};	/* Possible IRQs are 0-15 */
+static struct Scsi_Host *wd7000_host[IRQS];
 
 /*
  * (linear) base address for ROM BIOS
@@ -828,7 +845,8 @@
     cli();
 
     memset(scb, 0, sizeof(Scb));
-    scb->next = scbfree;  scbfree = scb;
+    scb->next = scbfree;
+    scbfree = scb;
     freescbs++;
 
     restore_flags(flags);
@@ -969,6 +987,7 @@
 #ifdef DEBUG
     printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
 #endif
+
     SCpnt->SCp.phase = 0;
 }
 
@@ -982,7 +1001,7 @@
     register Scb *scb;             /* for SCSI commands */
     register IcbAny *icb;          /* for host commands */
     register Scsi_Cmnd *SCpnt;
-    Adapter *host = irq2host[irq];  /* This MUST be set!!! */
+    Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */
     Mailbox *icmbs = host->mb.icmb;
 
 #ifdef DEBUG
@@ -990,6 +1009,7 @@
 #endif
 
     flag = inb(host->iobase+ASC_INTR_STAT);
+
 #ifdef DEBUG
     printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag);
 #endif
@@ -1264,6 +1284,144 @@
 }
 
 
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+{
+    unsigned long flags;
+
+    save_flags (flags);
+    cli ();
+
+#ifdef DEBUG
+    printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+#endif
+
+    /*
+     * Currently this is a no-op
+     */
+    printk ("Sorry, this function is currently out of order...\n");
+
+    restore_flags (flags);
+
+    return (length);
+}
+
+
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+    struct Scsi_Host *host = NULL;
+    Scsi_Device *scd = scsi_devices;
+    Adapter *adapter;
+    Mailbox *ogmbs, *icmbs;
+    unsigned long flags;
+    char *pos = buffer;
+    short i;
+
+#ifdef DEBUG
+    short count;
+#endif
+
+    /*
+     * Find the specified host board.
+     */
+    for (i = 0; i < IRQS; i++)
+        if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
+            host = wd7000_host[i];
+
+	    break;
+	}
+
+    /*
+     * Host not found!
+     */
+    if (! host)
+        return (-ESRCH);
+
+    /*
+     * Has data been written to the file ?
+     */ 
+    if (inout)
+        return (wd7000_set_info (buffer, length, host));
+
+    adapter = (Adapter *) host->hostdata;
+    ogmbs = adapter->mb.ogmb;
+    icmbs = adapter->mb.icmb;
+
+    save_flags (flags);
+    cli ();
+
+    SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
+    SPRINTF ("  IO base:     0x%x\n", adapter->iobase);
+    SPRINTF ("  IRQ:         %d\n", adapter->irq);
+    SPRINTF ("  DMA channel: %d\n", adapter->dma);
+
+#ifdef DEBUG
+    SPRINTF ("Control port value: 0x%x\n", adapter->control);
+    SPRINTF ("Incoming mailbox:\n");
+    SPRINTF ("  size: %d\n", ICMB_CNT);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < ICMB_CNT; i++)
+        if (icmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+
+    SPRINTF ("Outgoing mailbox:\n");
+    SPRINTF ("  size: %d\n", OGMB_CNT);
+    SPRINTF ("  next message: 0x%x\n", adapter->next_ogmb);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < OGMB_CNT; i++)
+        if (ogmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+#endif
+
+    /*
+     * Display driver information for each device attached to the board.
+     */
+    SPRINTF ("Attached devices: %s\n", scd ? "" : "none");
+
+    for ( ; scd; scd = scd->next)
+        if (scd->host->host_no == hostno) {
+	    SPRINTF ("  [Channel: %02d, Id: %02d, Lun: %02d]  ",
+		     scd->channel, scd->id, scd->lun);
+	    SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+		     scsi_device_types[(short) scd->type] : "Unknown device");
+
+	    for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->vendor[i]);
+	    SPRINTF (" ");
+
+	    for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->model[i]);
+	    SPRINTF ("\n");
+        }
+
+    restore_flags (flags);
+
+    /*
+     * Calculate start of next buffer, and return value.
+     */
+    *start = buffer + offset;
+
+    if ((pos - buffer) < offset)
+        return (0);
+    else if ((pos - buffer - offset) < length)
+        return (pos - buffer - offset);
+    else
+        return (length);
+}
+
+
 /*
  *  Returns the number of adapters this driver is supporting.
  *
@@ -1282,9 +1440,11 @@
     Adapter *host = NULL;
     struct Scsi_Host *sh;
 
+    for (i = 0; i < IRQS; wd7000_host[i++] = NULL);
     for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
 
     tpnt->proc_dir = &proc_scsi_wd7000;
+    tpnt->proc_info = &wd7000_proc_info;
 
     /*
      * Set up SCB free list, which is shared by all adapters
@@ -1292,6 +1452,9 @@
     init_scbs ();
 
     for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) {
+        /*
+	 * First, search for BIOS SIGNATURE...
+	 */
         for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
 	    for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
 	        for (i = 0; i < pass; i++)
@@ -1306,7 +1469,9 @@
 	    }
 
 bios_matched:
-
+	/*
+	 * BIOS SIGNATURE has been found.
+	 */
 #ifdef DEBUG
 	printk ("wd7000_detect: pass %d\n", pass + 1);
 
@@ -1335,7 +1500,6 @@
 #ifdef DEBUG
  	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
 #endif
-
  	    /*
   	     * ASC reset...
  	     */
@@ -1367,8 +1531,7 @@
 		host = (Adapter *) sh->hostdata;
 
 #ifdef DEBUG
-		printk ("wd7000_detect: adapter allocated at 0x%x\n",
-			(int) host);
+		printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
 #endif
 
 		memset (host, 0, sizeof (Adapter));
@@ -1382,9 +1545,8 @@
 		    host->dma = configs[cfg_ptr - 1].dma;
 		}
 
-	    	host->sh = sh;
+	    	host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
     		host->iobase = iobase;
-		irq2host[host->irq] = host;
 
 #ifdef DEBUG
 		printk ("wd7000_detect: Trying init WD-7000 card at IO "
@@ -1409,8 +1571,7 @@
 		request_region (host->iobase, 4, "wd7000");
 
 		/*
-		 *  For boards before rev 6.0, scatter/gather
-		 *  isn't supported.
+		 *  For boards before rev 6.0, scatter/gather isn't supported.
 		 */
 		if (host->rev1 < 6)
 		    sh->sg_tablesize = SG_NONE;
@@ -1429,8 +1590,7 @@
 
 #ifdef DEBUG
 	else
- 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n",
-		    iobase);
+ 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
 #endif
 
     }
@@ -1469,18 +1629,55 @@
 
 
 /*
- *  This was borrowed directly from aha1542.c, but my disks are organized
- *  this way, so I think it will work OK.  Someone who is ambitious can
- *  borrow a newer or more complete version from another driver.
- */
-int wd7000_biosparam(Disk * disk, kdev_t dev, int* ip)
-{
-  int size = disk->capacity;
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+ *  This was borrowed directly from aha1542.c. (Zaga)
+ */
+int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
+{
+#ifdef BIOSPARAM_DEBUG
+    printk("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
+#endif
+
+    /*
+     * try default translation
+     */
+    ip[0] = 64;
+    ip[1] = 32;
+    ip[2] = disk->capacity / (64 * 32);
+
+    /*
+     * for disks >1GB do some guessing
+     */
+    if (ip[2] >= 1024) {
+	int info[3];
+
+	/*
+	 * try to figure out the geometry from the partition table
+	 */
+	if ((scsicam_bios_param (disk, dev, info) < 0) ||
+	    !(((info[0] == 64) && (info[1] == 32)) ||
+	    ((info[0] == 255) && (info[1] == 63)))) {
+	    printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n"
+		    "                  using extended translation.\n");
+
+	    ip[0] = 255;
+	    ip[1] = 63;
+	    ip[2] = disk->capacity / (255 * 63);
+	} else {
+	    ip[0] = info[0];
+	    ip[1] = info[1];
+	    ip[2] = info[2];
+
+	    if (info[0] == 255)
+		printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+	}
+    }
+
+#ifdef BIOSPARAM_DEBUG
+    printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+    printk ("WARNING: check, if the bios geometry is correct.\n");
+#endif
+
+    return (0);
 }
 
 #ifdef MODULE
diff -urN linux-2.0.31-pre3/drivers/scsi/wd7000.h linux-2.0.31-pre4/drivers/scsi/wd7000.h
--- linux-2.0.31-pre3/drivers/scsi/wd7000.h	1997-03-11 15:40:02.000000000 -0800
+++ linux-2.0.31-pre4/drivers/scsi/wd7000.h	2003-08-15 15:03:56.000000000 -0700
@@ -8,17 +8,20 @@
  * This file has been reduced to only the definitions needed for the
  * WD7000 host structure.
  *
+ * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr>  Jun 1997.
  */
 
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
-int wd7000_detect(Scsi_Host_Template *);
-int wd7000_command(Scsi_Cmnd *);
-int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd7000_abort(Scsi_Cmnd *);
-int wd7000_reset(Scsi_Cmnd *);
-int wd7000_biosparam(Disk *, kdev_t, int *);
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host);
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+int wd7000_detect (Scsi_Host_Template *);
+int wd7000_command (Scsi_Cmnd *);
+int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd7000_abort (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *);
+int wd7000_biosparam (Disk *, kdev_t, int *);
 
 #ifndef NULL
 #define NULL 0L
diff -urN linux-2.0.31-pre3/fs/buffer.c linux-2.0.31-pre4/fs/buffer.c
--- linux-2.0.31-pre3/fs/buffer.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/fs/buffer.c	2003-08-15 15:03:56.000000000 -0700
@@ -593,6 +593,8 @@
 	return NULL;
 }
 
+extern void allow_interrupts(void);
+
 static void refill_freelist(int size)
 {
 	struct buffer_head * bh;
@@ -616,6 +618,7 @@
 	}
 
 repeat:
+	allow_interrupts();
 	if(needed <= 0)
 		return;
 
@@ -669,6 +672,13 @@
 		};
 	}
 
+	/*
+	 * In order to protect our reserved pages, 
+	 * return now if we got any buffers.
+	 */
+	if (free_list[BUFSIZE_INDEX(size)])
+		return;
+
 	/* and repeat until we find something good */
 	if (!grow_buffers(GFP_ATOMIC, size))
 		wakeup_bdflush(1);
@@ -697,6 +707,7 @@
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
 repeat:
+	allow_interrupts();
 	bh = get_hash_table(dev, block, size);
 	if (bh) {
 		if (!buffer_dirty(bh)) {
@@ -709,6 +720,7 @@
 	}
 
 	while(!free_list[isize]) {
+		allow_interrupts();
 		refill_freelist(size);
 	}
 	
@@ -1506,6 +1518,7 @@
 		ndirty = 0;
 		nwritten = 0;
 	repeat:
+		allow_interrupts();
 
 		bh = lru_list[nlist];
 		if(bh) 
@@ -1646,6 +1659,7 @@
 			 ndirty = 0;
 			 refilled = 0;
 		 repeat:
+			 allow_interrupts();
 
 			 bh = lru_list[nlist];
 			 if(bh) 
diff -urN linux-2.0.31-pre3/fs/ext2/dir.c linux-2.0.31-pre4/fs/ext2/dir.c
--- linux-2.0.31-pre3/fs/ext2/dir.c	1995-12-22 03:00:19.000000000 -0800
+++ linux-2.0.31-pre4/fs/ext2/dir.c	2003-08-15 15:03:56.000000000 -0700
@@ -206,9 +206,6 @@
 		offset = 0;
 		brelse (bh);
 	}
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	return 0;
 }
diff -urN linux-2.0.31-pre3/fs/ext2/inode.c linux-2.0.31-pre4/fs/ext2/inode.c
--- linux-2.0.31-pre3/fs/ext2/inode.c	1997-03-11 13:46:41.000000000 -0800
+++ linux-2.0.31-pre4/fs/ext2/inode.c	2003-08-15 15:03:56.000000000 -0700
@@ -504,6 +504,8 @@
 		inode->i_flags |= S_APPEND;
 	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)
 		inode->i_flags |= S_IMMUTABLE;
+	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL)
+		inode->i_flags |= MS_NOATIME;
 }
 
 static int ext2_update_inode(struct inode * inode, int do_sync)
diff -urN linux-2.0.31-pre3/fs/ext2/ioctl.c linux-2.0.31-pre4/fs/ext2/ioctl.c
--- linux-2.0.31-pre3/fs/ext2/ioctl.c	1996-07-02 09:08:42.000000000 -0700
+++ linux-2.0.31-pre4/fs/ext2/ioctl.c	2003-08-15 15:03:56.000000000 -0700
@@ -60,6 +60,10 @@
 			inode->i_flags |= S_IMMUTABLE;
 		else
 			inode->i_flags &= ~S_IMMUTABLE;
+		if (flags & EXT2_NOATIME_FL)
+			inode->i_flags |= MS_NOATIME;
+		else
+			inode->i_flags &= ~MS_NOATIME;
 		inode->i_ctime = CURRENT_TIME;
 		inode->i_dirt = 1;
 		return 0;
diff -urN linux-2.0.31-pre3/fs/ext2/symlink.c linux-2.0.31-pre4/fs/ext2/symlink.c
--- linux-2.0.31-pre3/fs/ext2/symlink.c	1996-04-12 00:33:26.000000000 -0700
+++ linux-2.0.31-pre4/fs/ext2/symlink.c	2003-08-15 15:03:56.000000000 -0700
@@ -86,10 +86,7 @@
 		link = bh->b_data;
 	} else
 		link = (char *) inode->u.ext2_i.i_data;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	current->link_count++;
 	error = open_namei (link, flag, mode, res_inode, dir);
 	current->link_count--;
@@ -127,10 +124,7 @@
 		i++;
 		put_user (c, buffer++);
 	}
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	iput (inode);
 	if (bh)
 		brelse (bh);
diff -urN linux-2.0.31-pre3/fs/file_table.c linux-2.0.31-pre4/fs/file_table.c
--- linux-2.0.31-pre3/fs/file_table.c	1996-08-05 00:13:54.000000000 -0700
+++ linux-2.0.31-pre4/fs/file_table.c	2003-08-15 15:03:56.000000000 -0700
@@ -5,6 +5,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/stddef.h>
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -129,11 +130,12 @@
 	do {
 		for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
 			if (!f->f_count) {
-				remove_file_free(f);
-				memset(f,0,sizeof(*f));
-				put_last_free(f);
+				/* The f_next pointer is followed by the f_prev pointer */
+				memset(f, 0, offsetof(struct file, f_next));
+				memset(&f->f_prev + 1, 0, sizeof(*f) - sizeof(f->f_prev) - offsetof(struct file, f_prev));
 				f->f_count = 1;
 				f->f_version = ++event;
+				first_file = f->f_next;
 				return f;
 			}
 	} while (nr_files < max && grow_files());
diff -urN linux-2.0.31-pre3/fs/ncpfs/dir.c linux-2.0.31-pre4/fs/ncpfs/dir.c
--- linux-2.0.31-pre3/fs/ncpfs/dir.c	1996-11-30 02:21:21.000000000 -0800
+++ linux-2.0.31-pre4/fs/ncpfs/dir.c	2003-08-15 15:03:56.000000000 -0700
@@ -1067,7 +1067,6 @@
 	}
         if (ncp_find_dir_inode(dir, name) != NULL)
 	{
-		iput(dir);
                 error = -EBUSY;
         }
 	else
@@ -1115,7 +1114,6 @@
 	}
         if (ncp_find_dir_inode(dir, name) != NULL)
 	{
-		iput(dir);
                 error = -EBUSY;
         }
 	else
diff -urN linux-2.0.31-pre3/fs/ncpfs/inode.c linux-2.0.31-pre4/fs/ncpfs/inode.c
--- linux-2.0.31-pre3/fs/ncpfs/inode.c	1996-11-30 02:21:21.000000000 -0800
+++ linux-2.0.31-pre4/fs/ncpfs/inode.c	2003-08-15 15:03:56.000000000 -0700
@@ -125,27 +125,36 @@
 	}
 }
 
+/*
+ * Defer release of inode_info and file_info structures until the inode
+ * has been cleared.  This avoids a race condition allowing the inode to 
+ * be put back in use before being cleared. Also, temporarily increment
+ * i_count after clear_inode() so that the inode can't be reused.
+ */
 static void
 ncp_put_inode(struct inode *inode)
 {
-        struct nw_file_info *finfo = NCP_FINFO(inode);
-	struct super_block *sb = inode->i_sb;
-
+	struct super_block	*sb	= inode->i_sb;
+	struct ncp_server	*server	= NCP_SERVER(inode);
+	struct ncp_inode_info	*iinfo	= NCP_INOP(inode);
+        struct nw_file_info	*finfo	= NCP_FINFO(inode);
+
+	/*
+	 * This operation may block, so we lock before checking the count.
+	 */
 	lock_super(sb);
-        if (finfo->opened != 0)
-	{
-                if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle)!=0)
-		{
-                        /* We can't do anything but complain. */
-                        printk("ncp_put_inode: could not close\n");
-                }
-        }
 
+	if (inode->i_count > 1) {
+printk("ncp_put_inode: inode in use device %s, inode %ld, count=%d\n", 
+kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+		goto unlock;
+	}
+	
 	DDPRINTK("ncp_put_inode: put %s\n",
-		finfo->i.entryName);
-
-        ncp_free_inode_info(NCP_INOP(inode));
-
+		 finfo->i.entryName);
+	/*
+	 * This operation should never block.
+	 */
         if (S_ISDIR(inode->i_mode))
 	{
                 DDPRINTK("ncp_put_inode: put directory %ld\n",
@@ -154,6 +163,28 @@
         }                
 
 	clear_inode(inode);
+
+	/*
+	 * After clearing the inode i_count will be 0 in 2.0.xx kernels.
+	 * To keep the inode from being reused as free if we block while
+	 * closing the file, increment i_count temporarily.
+	 */
+	inode->i_count++;
+
+	if (finfo->opened != 0)
+	{
+		if (ncp_close_file(server, finfo->file_handle) != 0)
+		{
+			/* We can't do anything but complain. */
+			printk("ncp_put_inode: could not close %s\n",
+		 		finfo->i.entryName);
+		}
+	}
+
+	ncp_free_inode_info(iinfo);
+	inode->i_count--;
+		
+unlock:
 	unlock_super(sb);
 }
 
diff -urN linux-2.0.31-pre3/fs/pipe.c linux-2.0.31-pre4/fs/pipe.c
--- linux-2.0.31-pre3/fs/pipe.c	1996-07-07 10:27:04.000000000 -0700
+++ linux-2.0.31-pre4/fs/pipe.c	2003-08-15 15:03:56.000000000 -0700
@@ -68,7 +68,7 @@
 	PIPE_LOCK(*inode)--;
 	wake_up_interruptible(&PIPE_WAIT(*inode));
 	if (read) {
-	        inode->i_atime = CURRENT_TIME;
+		UPDATE_ATIME(inode);
 		return read;
 	}
 	if (PIPE_WRITERS(*inode))
diff -urN linux-2.0.31-pre3/fs/stat.c linux-2.0.31-pre4/fs/stat.c
--- linux-2.0.31-pre3/fs/stat.c	1996-02-16 23:19:41.000000000 -0800
+++ linux-2.0.31-pre4/fs/stat.c	2003-08-15 15:03:56.000000000 -0700
@@ -235,5 +235,6 @@
 		iput(inode);
 		return -EINVAL;
 	}
+	UPDATE_ATIME(inode);
 	return inode->i_op->readlink(inode,buf,bufsiz);
 }
diff -urN linux-2.0.31-pre3/fs/super.c linux-2.0.31-pre4/fs/super.c
--- linux-2.0.31-pre3/fs/super.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/fs/super.c	2003-08-15 15:03:56.000000000 -0700
@@ -278,6 +278,7 @@
 	{ MS_NODEV, ",nodev" },
 	{ MS_SYNCHRONOUS, ",sync" },
 	{ MS_MANDLOCK, ",mand" },
+	{ MS_NOATIME, ",noatime" },
 #ifdef MS_NOSUB			/* Can't find this except in mount.c */
 	{ MS_NOSUB, ",nosub" },
 #endif
diff -urN linux-2.0.31-pre3/include/asm-i386/irq.h linux-2.0.31-pre4/include/asm-i386/irq.h
--- linux-2.0.31-pre3/include/asm-i386/irq.h	1997-03-05 11:20:45.000000000 -0800
+++ linux-2.0.31-pre4/include/asm-i386/irq.h	2003-08-15 15:03:56.000000000 -0700
@@ -144,12 +144,16 @@
 	"movl 32(%edx), %eax\n\t" \
 	"shrl $24,%eax\n\t" \
 	"andb $0x0F,%al\n"
-	
+
 #define	ENTER_KERNEL \
 	"pushl %eax\n\t" \
+	"pushl %ebx\n\t" \
+	"pushl %ecx\n\t" \
 	"pushl %edx\n\t" \
 	"pushfl\n\t" \
 	"cli\n\t" \
+	"movl $6000, %ebx\n\t" \
+	"movl "SYMBOL_NAME_STR(smp_loops_per_tick)", %ecx\n\t" \
 	GET_PROCESSOR_ID \
 	"btsl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \
 	"1: " \
@@ -158,6 +162,7 @@
 	"jnc 3f\n\t" \
 	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
 	"je 4f\n\t" \
+	"movb $1, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
 	"2: " \
         SMP_PROF_INT_SPINS \
 	"btl %al, "SYMBOL_NAME_STR(smp_invalidate_needed)"\n\t" \
@@ -168,14 +173,28 @@
 	"movl %cr3,%edx\n\t" \
 	"movl %edx,%cr3\n" \
 	"5: btl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
-	"jc 2b\n\t" \
-	"jmp 1b\n\t" \
+	"jnc 1b\n\t" \
+	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
+	"je 4f\n\t" \
+	"decl %ecx\n\t" \
+	"jne 2b\n\t" \
+	"decl %ebx\n\t" \
+	"jne 6f\n\t" \
+	"call "SYMBOL_NAME_STR(irq_deadlock_detected)"\n\t" \
+	"6: movl "SYMBOL_NAME_STR(smp_loops_per_tick)", %ecx\n\t" \
+	"cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \
+	"jne 2b\n\t" \
+	"incl "SYMBOL_NAME_STR(jiffies)"\n\t" \
+	"jmp 2b\n\t" \
 	"3: " \
 	"movb %al, "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
 	"4: " \
 	"incl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
+	"movb $0, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
 	"popfl\n\t" \
 	"popl %edx\n\t" \
+	"popl %ecx\n\t" \
+	"popl %ebx\n\t" \
 	"popl %eax\n\t"
 
 #define	LEAVE_KERNEL \
@@ -185,7 +204,10 @@
 	"cli\n\t" \
 	"decl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
 	"jnz 1f\n\t" \
-	"movb $" STR (NO_PROC_ID) ", "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+	"movb "SYMBOL_NAME_STR(saved_active_kernel_processor)",%al\n\t" \
+	"movb %al,"SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+	"cmpb $" STR (NO_PROC_ID) ",%al\n\t" \
+	"jne 1f\n\t" \
 	"lock\n\t" \
 	"btrl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
 	"1: " \
diff -urN linux-2.0.31-pre3/include/linux/atalk.h linux-2.0.31-pre4/include/linux/atalk.h
--- linux-2.0.31-pre3/include/linux/atalk.h	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre4/include/linux/atalk.h	2003-08-15 15:03:56.000000000 -0700
@@ -8,6 +8,8 @@
 #ifndef __LINUX_ATALK_H__
 #define __LINUX_ATALK_H__
 
+#define SIOCATALKDIFADDR      (SIOCPROTOPRIVATE + 0)
+
 #define ATPORT_FIRST	1
 #define ATPORT_RESERVED	128
 #define ATPORT_LAST	255
diff -urN linux-2.0.31-pre3/include/linux/ext2_fs.h linux-2.0.31-pre4/include/linux/ext2_fs.h
--- linux-2.0.31-pre3/include/linux/ext2_fs.h	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre4/include/linux/ext2_fs.h	2003-08-15 15:03:56.000000000 -0700
@@ -189,6 +189,7 @@
 #define EXT2_IMMUTABLE_FL		0x00000010 /* Immutable file */
 #define EXT2_APPEND_FL			0x00000020 /* writes to file may only append */
 #define EXT2_NODUMP_FL			0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL			0x00000080 /* do not update atime */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 	
 /*
diff -urN linux-2.0.31-pre3/include/linux/fs.h linux-2.0.31-pre4/include/linux/fs.h
--- linux-2.0.31-pre3/include/linux/fs.h	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/include/linux/fs.h	2003-08-15 15:03:56.000000000 -0700
@@ -73,6 +73,7 @@
 #define S_WRITE		128	/* Write on file/directory/symlink */
 #define S_APPEND	256	/* Append-only file */
 #define S_IMMUTABLE	512	/* Immutable file */
+#define MS_NOATIME	1024	/* Do not update access times. */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -103,6 +104,13 @@
 #define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+#define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME)
+
+#define UPDATE_ATIME(inode) \
+	if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \
+		inode->i_atime = CURRENT_TIME; \
+		inode->i_dirt = 1; \
+	}
 
 /* the read-only stuff doesn't really belong here, but any other place is
    probably as bad and I don't want to create yet another include file. */
diff -urN linux-2.0.31-pre3/include/linux/pci.h linux-2.0.31-pre4/include/linux/pci.h
--- linux-2.0.31-pre3/include/linux/pci.h	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/include/linux/pci.h	2003-08-15 15:03:56.000000000 -0700
@@ -324,6 +324,7 @@
 
 #define PCI_VENDOR_ID_HP		0x103c
 #define PCI_DEVICE_ID_HP_J2585A		0x1030
+#define PCI_DEVICE_ID_HP_J2585B		0x1031
 
 #define PCI_VENDOR_ID_PCTECH		0x1042
 #define PCI_DEVICE_ID_PCTECH_RZ1000	0x1000
@@ -512,6 +513,7 @@
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
 #define PCI_VENDOR_ID_COMPEX		0x11f6
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4	0x0112
 #define PCI_DEVICE_ID_COMPEX_RL2000	0x1401
 
 #define PCI_VENDOR_ID_RP               0x11fe
diff -urN linux-2.0.31-pre3/include/net/ip_masq.h linux-2.0.31-pre4/include/net/ip_masq.h
--- linux-2.0.31-pre3/include/net/ip_masq.h	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre4/include/net/ip_masq.h	2003-08-15 15:03:56.000000000 -0700
@@ -54,6 +54,8 @@
 #define IP_MASQ_F_CONTROL		0x100 	/* this is a control channel */
 #define IP_MASQ_F_NO_SPORT    		0x200	/* no sport set yet */
 #define IP_MASQ_F_FTP_PASV	    	0x400	/* ftp PASV command just issued */
+#define IP_MASQ_F_NO_REPLY		0x800 	/* no reply yet from outside */
+#define IP_MASQ_F_AFW_PORT	       0x1000
 
 #ifdef __KERNEL__
 
diff -urN linux-2.0.31-pre3/include/scsi/scsi_ioctl.h linux-2.0.31-pre4/include/scsi/scsi_ioctl.h
--- linux-2.0.31-pre3/include/scsi/scsi_ioctl.h	1996-05-08 21:38:41.000000000 -0700
+++ linux-2.0.31-pre4/include/scsi/scsi_ioctl.h	2003-08-15 15:03:56.000000000 -0700
@@ -5,6 +5,8 @@
 #define SCSI_IOCTL_TEST_UNIT_READY 2
 #define SCSI_IOCTL_BENCHMARK_COMMAND 3
 #define SCSI_IOCTL_SYNC 4			/* Request synchronous parameters */
+#define SCSI_IOCTL_START_UNIT 5
+#define SCSI_IOCTL_STOP_UNIT 6
 /* The door lock/unlock constants are compatible with Sun constants for
    the cdrom */
 #define SCSI_IOCTL_DOORLOCK 0x5380		/* lock the eject mechanism */
diff -urN linux-2.0.31-pre3/init/main.c linux-2.0.31-pre4/init/main.c
--- linux-2.0.31-pre3/init/main.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/init/main.c	2003-08-15 15:03:56.000000000 -0700
@@ -486,6 +486,10 @@
    still work even if initially too large, it will just take slightly longer */
 unsigned long loops_per_sec = (1<<12);
 
+#if defined(__SMP__) && defined(__i386__)
+unsigned long smp_loops_per_tick = 1000000;
+#endif
+
 /* This is the number of bits of precision for the loops_per_second.  Each
    bit takes on average 1.5/HZ seconds.  This (like the original) is a little
    better than 1% */
@@ -533,6 +537,10 @@
 	printk("ok - %lu.%02lu BogoMIPS\n",
 		(loops_per_sec+2500)/500000,
 		((loops_per_sec+2500)/5000) % 100);
+
+#if defined(__SMP__) && defined(__i386__)
+	smp_loops_per_tick = loops_per_sec / 400;
+#endif
 }
 
 static void parse_root_dev(char * line)
diff -urN linux-2.0.31-pre3/kernel/fork.c linux-2.0.31-pre4/kernel/fork.c
--- linux-2.0.31-pre3/kernel/fork.c	1997-04-03 20:02:28.000000000 -0800
+++ linux-2.0.31-pre4/kernel/fork.c	2003-08-15 15:03:56.000000000 -0700
@@ -127,9 +127,11 @@
 		tsk->cmin_flt = tsk->cmaj_flt = 0;
 		tsk->nswap = tsk->cnswap = 0;
 		if (new_page_tables(tsk))
-			return -1;
+			goto free_mm;
 		if (dup_mmap(mm)) {
 			free_page_tables(mm);
+free_mm:
+			kfree(mm);
 			return -1;
 		}
 		return 0;
diff -urN linux-2.0.31-pre3/kernel/sched.c linux-2.0.31-pre4/kernel/sched.c
--- linux-2.0.31-pre3/kernel/sched.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/kernel/sched.c	2003-08-15 15:03:56.000000000 -0700
@@ -283,6 +283,87 @@
 	return weight;
 }
 
+
+/*
+  The following allow_interrupts function is used to workaround a rare but
+  nasty deadlock situation that is possible for 2.0.x Intel SMP because it uses
+  a single kernel lock and interrupts are only routed to the boot CPU.  There
+  are two deadlock scenarios this code protects against.
+
+  The first scenario is that if a CPU other than the boot CPU holds the kernel
+  lock and needs to wait for an operation to complete that itself requires an
+  interrupt, there is a deadlock since the boot CPU may be able to accept the
+  interrupt but will not be able to acquire the kernel lock to process it.
+
+  The workaround for this deadlock requires adding calls to allow_interrupts to
+  places where this deadlock is possible.  These places are known to be present
+  in buffer.c and keyboard.c.  It is also possible that there are other such
+  places which have not been identified yet.  In order to break the deadlock,
+  the code in allow_interrupts temporarily yields the kernel lock directly to
+  the boot CPU to allow the interrupt to be processed.  The boot CPU interrupt
+  entry code indicates that it is spinning waiting for the kernel lock by
+  setting the smp_blocked_interrupt_pending variable.  This code notices that
+  and manipulates the active_kernel_processor variable to yield the kernel lock
+  without ever clearing it.  When the interrupt has been processed, the
+  saved_active_kernel_processor variable contains the value for the interrupt
+  exit code to restore, either the APICID of the CPU that granted it the kernel
+  lock, or NO_PROC_ID in the normal case where no yielding occurred.  Restoring
+  active_kernel_processor from saved_active_kernel_processor returns the kernel
+  lock back to the CPU that yielded it.
+
+  The second form of deadlock is even more insidious.  Suppose the boot CPU
+  takes a page fault and then the previous scenario ensues.  In this case, the
+  boot CPU would spin with interrupts disabled waiting to acquire the kernel
+  lock.  To resolve this deadlock, the kernel lock acquisition code must enable
+  interrupts briefly so that the pending interrupt can be handled as in the
+  case above.
+
+  An additional form of deadlock is where kernel code running on a non-boot CPU
+  waits for the jiffies variable to be incremented.  This deadlock is avoided
+  by having the spin loops in ENTER_KERNEL increment jiffies approximately
+  every 10 milliseconds.  Finally, if approximately 60 seconds elapse waiting
+  for the kernel lock, a message will be printed if possible to indicate that a
+  deadlock has been detected.
+
+		Leonard N. Zubkoff
+		   4 August 1997
+*/
+
+#if defined(__SMP__) && defined(__i386__)
+
+volatile unsigned char smp_blocked_interrupt_pending = 0;
+
+volatile unsigned char saved_active_kernel_processor = NO_PROC_ID;
+
+void allow_interrupts(void)
+{
+  if (smp_processor_id() == boot_cpu_id) return;
+  if (smp_blocked_interrupt_pending)
+    {
+      long timeout_counter = loops_per_sec;
+      unsigned long saved_kernel_counter;
+      saved_active_kernel_processor = active_kernel_processor;
+      saved_kernel_counter = kernel_counter;
+      kernel_counter = 0;
+      active_kernel_processor = boot_cpu_id;
+      while (active_kernel_processor != saved_active_kernel_processor &&
+	     --timeout_counter >= 0)
+	barrier();
+      if (timeout_counter < 0)
+	panic("FORWARDED INTERRUPT TIMEOUT (AKP = %d, Saved AKP = %d)\n",
+	      active_kernel_processor, saved_active_kernel_processor);
+      kernel_counter = saved_kernel_counter;
+      saved_active_kernel_processor = NO_PROC_ID;
+    }
+}
+
+#else
+
+void allow_interrupts(void) {}
+
+#endif
+
+
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
  * scheduler: it's not perfect, but certainly works for most things.
@@ -303,6 +384,8 @@
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
 
+	allow_interrupts();
+
 	if (intr_count)
 		goto scheduling_in_interrupt;
 
@@ -1500,10 +1583,10 @@
 	error = verify_area(VERIFY_WRITE, interval, sizeof(struct timespec));
 	if (error)
 		return error;
-	
+
+	/* Values taken from 2.1.38 */	
 	t.tv_sec = 0;
-	t.tv_nsec = 0;   /* <-- Linus, please fill correct value in here */
-	return -ENOSYS;  /* and then delete this line. Thanks!           */
+	t.tv_nsec = 150000;   /* is this right for non-intel architecture too?*/
 	memcpy_tofs(interval, &t, sizeof(struct timespec));
 
 	return 0;
diff -urN linux-2.0.31-pre3/MAINTAINERS linux-2.0.31-pre4/MAINTAINERS
--- linux-2.0.31-pre3/MAINTAINERS	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/MAINTAINERS	2003-08-15 15:03:56.000000000 -0700
@@ -128,6 +128,12 @@
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
+IP MASQUERADING (IPV4)
+P:	Nigel Metheringham
+M:	Nigel.Metheringham@ThePLAnet.net
+L:	masq@indyramp.com
+S:	Maintained
+
 BUSLOGIC SCSI DRIVER
 P:	Leonard N. Zubkoff
 M:	Leonard N. Zubkoff <lnz@dandelion.com>
diff -urN linux-2.0.31-pre3/mm/filemap.c linux-2.0.31-pre4/mm/filemap.c
--- linux-2.0.31-pre3/mm/filemap.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/mm/filemap.c	2003-08-15 15:03:56.000000000 -0700
@@ -736,10 +736,7 @@
 	filp->f_reada = 1;
 	if (page_cache)
 		free_page(page_cache);
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode)
 	if (!read)
 		read = error;
 	return read;
@@ -1175,10 +1172,7 @@
 		return -EACCES;
 	if (!inode->i_op || !inode->i_op->readpage)
 		return -ENOEXEC;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode)
 	vma->vm_inode = inode;
 	inode->i_count++;
 	vma->vm_ops = ops;
diff -urN linux-2.0.31-pre3/net/appletalk/ddp.c linux-2.0.31-pre4/net/appletalk/ddp.c
--- linux-2.0.31-pre3/net/appletalk/ddp.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/net/appletalk/ddp.c	2003-08-15 15:03:56.000000000 -0700
@@ -854,6 +854,16 @@
 			((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_net=atif->address.s_net;
 			((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_node=ATADDR_BCAST;
 			break;
+	        case SIOCATALKDIFADDR:
+			if(!suser())
+				return -EPERM;
+			if(sa->sat_family!=AF_APPLETALK)
+				return -EINVAL;
+			if(atif==NULL)
+				return -EADDRNOTAVAIL;
+			atrtr_device_down(atif->dev);
+			atif_drop_device(atif->dev);
+			break;			
 	}
 	memcpy_tofs(arg,&atreq,sizeof(atreq));
 	return 0;
@@ -1995,6 +2005,7 @@
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
 		case SIOCGIFBRDADDR:
+		case SIOCATALKDIFADDR:		  
 			return atif_ioctl(cmd,(void *)arg);
 		/*
 		 *	Physical layer ioctl calls
diff -urN linux-2.0.31-pre3/net/ipv4/Config.in linux-2.0.31-pre4/net/ipv4/Config.in
--- linux-2.0.31-pre3/net/ipv4/Config.in	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/Config.in	2003-08-15 15:03:56.000000000 -0700
@@ -14,7 +14,9 @@
       bool 'IP: masquerading' CONFIG_IP_MASQUERADE
       if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
         comment 'Protocol-specific masquerading support will be built as modules.'
-        bool 'IP: ipautofw masq support' CONFIG_IP_MASQUERADE_IPAUTOFW
+        if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then	
+          bool 'IP: ipautofw masq support' CONFIG_IP_MASQUERADE_IPAUTOFW
+        fi
         bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
       fi
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then	
diff -urN linux-2.0.31-pre3/net/ipv4/ip_fw.c linux-2.0.31-pre4/net/ipv4/ip_fw.c
--- linux-2.0.31-pre3/net/ipv4/ip_fw.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/ip_fw.c	2003-08-15 15:03:56.000000000 -0700
@@ -895,6 +895,14 @@
 	struct ip_autofw * newaf;
 	init_timer(&af->timer);
 	newaf = kmalloc( sizeof(struct ip_autofw), GFP_ATOMIC );
+	if ( newaf == NULL ) 
+	{
+#ifdef DEBUG_IP_FIREWALL
+		printk("ip_autofw_add:  malloc said no\n");
+#endif
+		return( ENOMEM );
+	}
+
 	memcpy(newaf, af, sizeof(struct ip_autofw));
 	newaf->timer.data = (unsigned long) newaf;
 	newaf->timer.function = ip_autofw_expire;
diff -urN linux-2.0.31-pre3/net/ipv4/ip_masq_app.c linux-2.0.31-pre4/net/ipv4/ip_masq_app.c
--- linux-2.0.31-pre3/net/ipv4/ip_masq_app.c	1996-06-28 08:49:55.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/ip_masq_app.c	2003-08-15 15:03:56.000000000 -0700
@@ -15,6 +15,7 @@
  * Fixes:
  *	JJC			: Implemented also input pkt hook
  *	Miquel van Smoorenburg	: Copy more stuff when resizing skb
+ *	Harald Hoyer/James R. Leu: Additional ipautofw support
  *
  *
  * FIXME:
@@ -186,7 +187,11 @@
 struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
 {
         struct ip_masq_app * mapp;
-        mapp = ip_masq_app_get(ms->protocol, ms->dport);
+	mapp = ip_masq_app_get(ms->protocol, ms->dport);
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+	if (mapp == NULL)
+		mapp = ip_masq_app_get(ms->protocol, ms->sport);
+#endif
         if (mapp != NULL) {
                 /*
                  *	don't allow binding if already bound
diff -urN linux-2.0.31-pre3/net/ipv4/ip_masq.c linux-2.0.31-pre4/net/ipv4/ip_masq.c
--- linux-2.0.31-pre3/net/ipv4/ip_masq.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/ip_masq.c	2003-08-15 15:03:56.000000000 -0700
@@ -19,7 +19,7 @@
  *	Keith Owens		:	Keep control channels alive if any related data entries.
  *	Delian Delchev		:	Added support for ICMP requests and replys
  *	Nigel Metheringham	:	ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
- *
+ *	Juan Jose Ciarlante	:	re-assign maddr if no packet received from outside
  *	
  */
 
@@ -632,13 +632,21 @@
         ms->app_data	   = NULL;
 	ms->control	   = NULL;
 
-        if (proto == IPPROTO_UDP)
+        if (proto == IPPROTO_UDP && !matchport)
                 ms->flags |= IP_MASQ_F_NO_DADDR;
         
         /* get masq address from rif */
         ms->maddr	   = dev->pa_addr;
+        /*
+         *	Setup new entry as not replied yet.
+         *	This flag will allow masq. addr (ms->maddr)
+         *	to follow forwarding interface address.
+         */
+        ms->flags         |= IP_MASQ_F_NO_REPLY;
 
-        for (ports_tried = 0; ports_tried < *free_ports_p; ports_tried++){
+        for (ports_tried = 0; 
+	     (*free_ports_p && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN)));
+	     ports_tried++){
                 save_flags(flags);
                 cli();
                 
@@ -659,7 +667,7 @@
                  */
                 
                 mst = ip_masq_getbym(proto, ms->maddr, ms->mport);
-                if (mst == NULL) {
+                if (mst == NULL || matchport) {
                         save_flags(flags);
                         cli();
                 
@@ -752,7 +760,27 @@
         ms = ip_masq_out_get(iph);
 	if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
-
+                
+                /*
+                 *	If sysctl !=0 and no pkt has been received yet
+                 *	in this tunnel and routing iface address has changed...
+                 *	 "You are welcome, diald".
+                 */
+                if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && dev->pa_addr != ms->maddr) {
+                        unsigned long flags;
+                        if (sysctl_ip_dynaddr > 1) {
+                                printk(KERN_INFO "ip_fw_masquerade(): change maddr from %s",
+                                       in_ntoa(ms->maddr));
+                                printk(" to %s\n", in_ntoa(dev->pa_addr));
+                        }
+                        save_flags(flags);
+                        cli();
+                        ip_masq_unhash(ms);
+                        ms->maddr = dev->pa_addr;
+                        ip_masq_hash(ms);
+                        restore_flags(flags);
+                }
+                
 		/*
 		 *      Set sport if not defined yet (e.g. ftp PASV).  Because
 		 *	masq entries are hashed on sport, unhash with old value
@@ -784,7 +812,6 @@
 	/*
 	 *	Nope, not found, create a new entry for it
 	 */
-	
 	if (ms==NULL)
 	{
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
@@ -942,6 +969,27 @@
 		}
 		ip_masq_set_expire(ms, 0);
 		/* Rewrite source address */
+                
+                /*
+                 *	If sysctl !=0 and no pkt has been received yet
+                 *	in this tunnel and routing iface address has changed...
+                 *	 "You are welcome, diald".
+                 */
+                if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && dev->pa_addr != ms->maddr) {
+                        unsigned long flags;
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+                        printk(KERN_INFO "ip_fw_masq_icmp(): change masq.addr %s",
+                               in_ntoa(ms->maddr));
+                        printk("-> %s\n", in_ntoa(dev->pa_addr));
+#endif
+                        save_flags(flags);
+                        cli();
+                        ip_masq_unhash(ms);
+                        ms->maddr = dev->pa_addr;
+                        ip_masq_hash(ms);
+                        restore_flags(flags);
+                }
+                
 		iph->saddr = ms->maddr;
 		ip_send_check(iph);
 		/* Rewrite port (id) */
@@ -1042,7 +1090,7 @@
 	if (ip_compute_csum((unsigned char *) icmph, len)) 
 	{
 		/* Failed checksum! */
-		printk(KERN_INFO "MASQ: forward ICMP: failed checksum from %s!\n", 
+		printk(KERN_DEBUG "MASQ: forward ICMP: failed checksum from %s!\n", 
 		       in_ntoa(iph->saddr));
 		return(-1);
 	}
@@ -1133,6 +1181,11 @@
 			return 0;
 
 		ip_masq_set_expire(ms,0);
+                
+                /*
+                 *	got reply, so clear flag
+                 */
+                ms->flags &= ~IP_MASQ_F_NO_REPLY;
 
 		/* Reset source address */
 		iph->daddr = ms->saddr;
@@ -1238,7 +1291,7 @@
 	if (ip_compute_csum((unsigned char *) icmph, len)) 
 	{
 		/* Failed checksum! */
-		printk(KERN_INFO "MASQ: reverse ICMP: failed checksum from %s!\n", 
+		printk(KERN_DEBUG "MASQ: reverse ICMP: failed checksum from %s!\n", 
 		       in_ntoa(iph->saddr));
 		return(-1);
 	}
@@ -1338,7 +1391,7 @@
 				if (csum_tcpudp_magic(iph->saddr, iph->daddr, len,
 						      iph->protocol, skb->csum))
 				{
-					printk(KERN_INFO "MASQ: failed TCP/UDP checksum from %s!\n", 
+					printk(KERN_DEBUG "MASQ: failed TCP/UDP checksum from %s!\n", 
 					       in_ntoa(iph->saddr));
 					return -1;
 				}
@@ -1364,20 +1417,27 @@
         ms = ip_masq_in_get(iph);
 
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
-        if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1],
-						    iph->protocol, 0))) {
+
+        if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1], iph->protocol, 0))) 
+	{
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+		printk("ip_autofw_check_range\n");
+#endif
         	ms = ip_masq_new_enh(dev, iph->protocol,
         			     af->where, portptr[1],
         			     iph->saddr, portptr[0],
         			     0,
         			     portptr[1]);
         }
-        if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], 
-						    iph->protocol)) ) {
+        if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], iph->protocol)) ) 
+	{
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+		printk("ip_autofw_check_port\n");
+#endif
         	ms = ip_masq_new_enh(dev, iph->protocol,
         			     af->where, htons(af->hidden),
         			     iph->saddr, portptr[0],
-        			     0,
+        			     IP_MASQ_F_AFW_PORT,
         			     htons(af->visible));
         }
 #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
@@ -1392,6 +1452,11 @@
 		ip_masq_set_expire(ms,0);
 
                 /*
+                 *	got reply, so clear flag
+                 */
+                ms->flags &= ~IP_MASQ_F_NO_REPLY;
+                
+                /*
                  *	Set dport if not defined yet.
                  */
 
@@ -1543,9 +1608,15 @@
 	
 	if (offset < 128) 
 	{
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+		sprintf(temp,
+			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d,%d)",
+			ip_masq_free_ports[0], ip_masq_free_ports[1], ip_masq_free_ports[2]); 
+#else /* !defined(CONFIG_IP_MASQUERADE_ICMP) */
 		sprintf(temp,
 			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d)",
 			ip_masq_free_ports[0], ip_masq_free_ports[1]); 
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
 		len = sprintf(buffer, "%-127s\n", temp);
 	}
 	pos = 128;
diff -urN linux-2.0.31-pre3/net/ipv4/ip_masq_cuseeme.c linux-2.0.31-pre4/net/ipv4/ip_masq_cuseeme.c
--- linux-2.0.31-pre3/net/ipv4/ip_masq_cuseeme.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/ip_masq_cuseeme.c	2003-08-15 15:03:56.000000000 -0700
@@ -2,7 +2,7 @@
  *		IP_MASQ_FTP CUSeeMe masquerading module
  *
  *
- * Version:	@(#)ip_masq_cuseeme.c 0.02   07/23/96
+ * Version:	@(#)$Id: ip_masq_cuseeme.c,v 1.1.2.2 1997/05/29 18:27:41 davem Exp $
  *
  * Author:	Richard Lynch
  *		
@@ -11,7 +11,13 @@
  *	Richard Lynch     	:	Updated patch to conform to new module
  *					specifications
  *	Nigel Metheringham	:	Multiple port support
- *	
+ *	Michael Owings		:	Fixed broken init code
+ *					Added code to update inbound
+ *					packets with correct local addresses.
+ *					Fixes audio and "chat" problems
+ *					Thanx to the CU-SeeMe Consortium for
+ *					technical docs
+ *
  *
  *
  *	This program is free software; you can redistribute it and/or
@@ -47,7 +53,38 @@
 #define DEBUG_CONFIG_IP_MASQ_CUSEEME 0
 #endif
 
-/* 
+#pragma pack(1)
+/* CU-SeeMe Data Header */
+typedef struct {
+	u_short 	dest_family;
+	u_short 	dest_port;
+	u_long  	dest_addr;
+	short 		family;
+	u_short 	port;
+	u_long 		addr;
+	u_long 		seq;
+	u_short 	msg;
+	u_short		data_type;
+	u_short		packet_len;
+} cu_header;
+
+/* Open Continue Header */
+typedef struct	{
+	cu_header	cu_head;
+	u_short 	client_count; /* Number of client info structs */
+	u_long		seq_no;
+	char		user_name[20];
+	char		stuff[4]; /* flags,  version stuff,  etc */
+}oc_header;
+
+/* client info structures */
+typedef struct {
+	u_long		address; /* Client address */
+	char	       	stuff[8]; /* Flags, pruning bitfield,  packet counts etc */
+} client_info;
+#pragma pack()
+
+/*
  * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
  * First port is set to the default port.
  */
@@ -74,20 +111,61 @@
 	struct sk_buff *skb = *skb_p;
 	struct iphdr *iph = skb->h.iph;
 	struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
-	struct cu_header {
-		char dest[8];
-		short family;
-		u_short port;
-		u_long addr;
-	} *cu_head;
+	cu_header *cu_head;
 	char *data=(char *)&uh[1];
-	
-	if (skb->len - ((unsigned char *) data - skb->h.raw) > 16)
+
+	if (skb->len - ((unsigned char *) data - skb->h.raw) >= sizeof(cu_header))
 	{
-		cu_head         = (struct cu_header *) data;
-/*		printk("CUSeeMe orig: %lX:%X\n",ntohl(cu_head->addr),ntohs(cu_head->port));*/
-		cu_head->port   = ms->mport;
+		cu_head         = (cu_header *) data;
+		/* cu_head->port   = ms->mport; */
+	        if( cu_head->addr )
 		cu_head->addr = (u_long) dev->pa_addr;
+#if DEBUG_CONFIG_IP_MASQ_CUSEEME	  
+	        if(ntohs(cu_head->data_type) == 257)
+	           printk(KERN_INFO "Sending talk packet!\n");
+#endif	  
+	}
+	return 0;
+}
+
+int
+masq_cuseeme_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+	struct sk_buff *skb = *skb_p;
+	struct iphdr *iph = skb->h.iph;
+	struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+	cu_header *cu_head;
+	oc_header	*oc;
+	client_info	*ci;
+	char *data=(char *)&uh[1];
+	u_short len = skb->len - ((unsigned char *) data - skb->h.raw);
+	int		i, off;
+
+	if (len >= sizeof(cu_header))
+	{
+		cu_head         = (cu_header *) data;
+		if(cu_head->dest_addr) /* Correct destination address */
+			cu_head->dest_addr = (u_long) ms->saddr;
+		if(ntohs(cu_head->data_type)==101 && len > sizeof(oc_header))
+		{
+			oc = (oc_header * ) data;
+			/* Spin (grovel) thru client_info structs till we find our own */
+		        off=sizeof(oc_header);
+			for(i=0;
+			    (i < oc->client_count && off+sizeof(client_info) <= len);
+			    i++)		    
+			{
+			        ci=(client_info *)(data+off);
+				if(ci->address==(u_long) dev->pa_addr)
+				{
+				        /* Update w/ our real ip address and exit */
+					ci->address = (u_long) ms->saddr;
+					break;
+				}
+			        else
+				   off+=sizeof(client_info);
+			}
+		}
 	}
 	return 0;
 }
@@ -100,7 +178,7 @@
         masq_cuseeme_init_1,	/* ip_masq_init_1 */
         masq_cuseeme_done_1,	/* ip_masq_done_1 */
         masq_cuseeme_out,	/* pkt_out */
-        NULL                    /* pkt_in */
+        masq_cuseeme_in    	/* pkt_in */
 };
 
 
@@ -119,12 +197,12 @@
 				return -ENOMEM;
 			memcpy(masq_incarnations[i], &ip_masq_cuseeme, sizeof(struct ip_masq_app));
 			if ((j = register_ip_masq_app(masq_incarnations[i], 
-						      IPPROTO_TCP, 
+						      IPPROTO_UDP,
 						      ports[i]))) {
 				return j;
 			}
 #if DEBUG_CONFIG_IP_MASQ_CUSEEME
-			printk("CuSeeMe: loaded support on port[%d] = %d\n",
+			printk(KERN_INFO "CuSeeMe: loaded support on port[%d] = %d\n",
 			       i, ports[i]);
 #endif
 		} else {
@@ -152,7 +230,7 @@
 				kfree(masq_incarnations[i]);
 				masq_incarnations[i] = NULL;
 #if DEBUG_CONFIG_IP_MASQ_CUSEEME
-				printk("CuSeeMe: unloaded support on port[%d] = %d\n",
+				printk(KERN_INFO "CuSeeMe: unloaded support on port[%d] = %d\n",
 				       i, ports[i]);
 #endif
 			}
@@ -174,7 +252,7 @@
 void cleanup_module(void)
 {
         if (ip_masq_cuseeme_done() != 0)
-                printk("ip_masq_cuseeme: can't remove module");
+                printk(KERN_INFO "ip_masq_cuseeme: can't remove module");
 }
 
 #endif /* MODULE */
diff -urN linux-2.0.31-pre3/net/ipv4/tcp.c linux-2.0.31-pre4/net/ipv4/tcp.c
--- linux-2.0.31-pre3/net/ipv4/tcp.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/tcp.c	2003-08-15 15:03:56.000000000 -0700
@@ -1838,7 +1838,10 @@
 		case TCP_CLOSE:
 		case TCP_LISTEN:
 			break;
-		case TCP_LAST_ACK:	/* Could have shutdown() then close()!*/
+		case TCP_LAST_ACK:	/* Could have shutdown() then close().
+					   Be careful not to send double fin. */
+			ns=TCP_LAST_ACK;
+			break;
 		case TCP_CLOSE_WAIT:	/* They have FIN'd us. We send our FIN and
 					   wait only for the ACK */
 			ns=TCP_LAST_ACK;
diff -urN linux-2.0.31-pre3/net/ipv4/tcp_input.c linux-2.0.31-pre4/net/ipv4/tcp_input.c
--- linux-2.0.31-pre3/net/ipv4/tcp_input.c	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/tcp_input.c	2003-08-15 15:03:56.000000000 -0700
@@ -266,7 +266,6 @@
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
-		   sk->state 		!= TCP_CLOSE		&& /* comment above is false */
 		   sk->rcv_saddr	== daddr)		   /* local address  */
 			goto hit; /* You sunk my battleship! */
 	sk = tcp_v4_lookup_longway(daddr, hnum);
@@ -589,7 +588,7 @@
 		if (!tcp_clearance(saddr)) {
 #endif
 			/* Only let this warning get printed once a minute. */
-			if (jiffies - warning_time > HZ) {
+			if (jiffies - warning_time > HZ*60) {
 				warning_time = jiffies;
 				printk(KERN_INFO "Warning: possible SYN flooding. Sending cookies.\n");
 			}
@@ -2263,6 +2262,12 @@
 #ifdef CONFIG_SYN_COOKIES
 retry_search:
 #endif
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		if (skb->redirport)
+			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+						 dev->pa_addr, skb->redirport);
+		else
+#endif
 		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
 		if (!sk)
 			goto no_tcp_socket;
diff -urN linux-2.0.31-pre3/net/ipv4/tcp_output.c linux-2.0.31-pre4/net/ipv4/tcp_output.c
--- linux-2.0.31-pre3/net/ipv4/tcp_output.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/ipv4/tcp_output.c	2003-08-15 15:03:56.000000000 -0700
@@ -1212,6 +1212,9 @@
 		win_size = skb->len - (((unsigned char *) th) - skb->data);
 		win_size -= th->doff * 4;
 
+		/* Don't send more than the offered window! */
+		win_size = min(win_size, sk->window_seq - sk->sent_seq);
+
 		/*
 		 *	Grab the data for a temporary frame
 		 */
diff -urN linux-2.0.31-pre3/net/netsyms.c linux-2.0.31-pre4/net/netsyms.c
--- linux-2.0.31-pre3/net/netsyms.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre4/net/netsyms.c	2003-08-15 15:03:56.000000000 -0700
@@ -42,7 +42,8 @@
 #if     defined(CONFIG_ULTRA)   ||      defined(CONFIG_WD80x3)          || \
         defined(CONFIG_EL2)     ||      defined(CONFIG_NE2000)          || \
         defined(CONFIG_E2100)   ||      defined(CONFIG_HPLAN_PLUS)      || \
-        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)
+        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ULTRA32)
 #include "../drivers/net/8390.h"
 #endif
 
@@ -111,7 +112,8 @@
 #if	defined(CONFIG_ULTRA)	||	defined(CONFIG_WD80x3)		|| \
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
-	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)
+	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ULTRA32)
 	/* If 8390 NIC support is built in, we will need these. */
 	X(ei_open),
 	X(ei_close),
diff -urN linux-2.0.31-pre3/scripts/Menuconfig linux-2.0.31-pre4/scripts/Menuconfig
--- linux-2.0.31-pre3/scripts/Menuconfig	2003-08-15 15:03:55.000000000 -0700
+++ linux-2.0.31-pre4/scripts/Menuconfig	2003-08-15 15:03:56.000000000 -0700
@@ -1144,17 +1144,17 @@
 
 _CONFIG="${2:-.config}"
 
-if [ ! -f "$_CONFIG" ]; then
-  _CONFIG=$DEFAULTS
+if [ -f "$_CONFIG" ]; then
+  DEFAULTS=$_CONFIG
 fi
 
 AUTOCONF_H="${3:-include/linux/autoconf.h}"
 
-if [ -f $_CONFIG ]
+if [ -f $DEFAULTS ]
 then
   echo
-  echo "Using defaults found in" $_CONFIG
-  load_config_file $_CONFIG
+  echo "Using defaults found in" $DEFAULTS
+  load_config_file $DEFAULTS
 else
   echo
   echo "No defaults found"
